From a9ba1c45a8fe581a9fcb21a6d85b121de08d5830 Mon Sep 17 00:00:00 2001 From: cere Date: Wed, 21 Feb 2024 02:46:57 -0500 Subject: [PATCH] cryptodome no worky on other platforms, try kodi's version --- addon.xml | 1 + resources/lib/deps/Cryptodome/Cipher/AES.py | 234 -- resources/lib/deps/Cryptodome/Cipher/AES.pyi | 156 - resources/lib/deps/Cryptodome/Cipher/ARC2.py | 175 -- resources/lib/deps/Cryptodome/Cipher/ARC2.pyi | 35 - resources/lib/deps/Cryptodome/Cipher/ARC4.py | 136 - resources/lib/deps/Cryptodome/Cipher/ARC4.pyi | 16 - .../lib/deps/Cryptodome/Cipher/Blowfish.py | 159 - .../lib/deps/Cryptodome/Cipher/Blowfish.pyi | 35 - resources/lib/deps/Cryptodome/Cipher/CAST.py | 159 - resources/lib/deps/Cryptodome/Cipher/CAST.pyi | 35 - .../lib/deps/Cryptodome/Cipher/ChaCha20.py | 287 -- .../lib/deps/Cryptodome/Cipher/ChaCha20.pyi | 25 - .../Cryptodome/Cipher/ChaCha20_Poly1305.py | 336 -- .../Cryptodome/Cipher/ChaCha20_Poly1305.pyi | 28 - resources/lib/deps/Cryptodome/Cipher/DES.py | 158 - resources/lib/deps/Cryptodome/Cipher/DES.pyi | 35 - resources/lib/deps/Cryptodome/Cipher/DES3.py | 187 -- resources/lib/deps/Cryptodome/Cipher/DES3.pyi | 37 - .../lib/deps/Cryptodome/Cipher/PKCS1_OAEP.py | 231 -- .../lib/deps/Cryptodome/Cipher/PKCS1_OAEP.pyi | 35 - .../lib/deps/Cryptodome/Cipher/PKCS1_v1_5.py | 189 -- .../lib/deps/Cryptodome/Cipher/PKCS1_v1_5.pyi | 20 - .../lib/deps/Cryptodome/Cipher/Salsa20.py | 167 - .../lib/deps/Cryptodome/Cipher/Salsa20.pyi | 26 - .../lib/deps/Cryptodome/Cipher/_ARC4.abi3.so | Bin 21016 -> 0 bytes .../deps/Cryptodome/Cipher/_EKSBlowfish.py | 131 - .../deps/Cryptodome/Cipher/_EKSBlowfish.pyi | 15 - .../deps/Cryptodome/Cipher/_Salsa20.abi3.so | Bin 27016 -> 0 bytes .../lib/deps/Cryptodome/Cipher/__init__.py | 79 - .../lib/deps/Cryptodome/Cipher/__init__.pyi | 0 .../deps/Cryptodome/Cipher/_chacha20.abi3.so | Bin 30624 -> 0 bytes .../lib/deps/Cryptodome/Cipher/_mode_cbc.py | 293 -- .../lib/deps/Cryptodome/Cipher/_mode_cbc.pyi | 25 - .../lib/deps/Cryptodome/Cipher/_mode_ccm.py | 650 ---- .../lib/deps/Cryptodome/Cipher/_mode_ccm.pyi | 47 - .../lib/deps/Cryptodome/Cipher/_mode_cfb.py | 293 -- .../lib/deps/Cryptodome/Cipher/_mode_cfb.pyi | 26 - .../lib/deps/Cryptodome/Cipher/_mode_ctr.py | 393 --- .../lib/deps/Cryptodome/Cipher/_mode_ctr.pyi | 27 - .../lib/deps/Cryptodome/Cipher/_mode_eax.py | 408 --- .../lib/deps/Cryptodome/Cipher/_mode_eax.pyi | 45 - .../lib/deps/Cryptodome/Cipher/_mode_ecb.py | 220 -- .../lib/deps/Cryptodome/Cipher/_mode_ecb.pyi | 19 - .../lib/deps/Cryptodome/Cipher/_mode_gcm.py | 620 ---- .../lib/deps/Cryptodome/Cipher/_mode_gcm.pyi | 45 - .../lib/deps/Cryptodome/Cipher/_mode_ocb.py | 532 ---- .../lib/deps/Cryptodome/Cipher/_mode_ocb.pyi | 36 - .../lib/deps/Cryptodome/Cipher/_mode_ofb.py | 282 -- .../lib/deps/Cryptodome/Cipher/_mode_ofb.pyi | 25 - .../deps/Cryptodome/Cipher/_mode_openpgp.py | 206 -- .../deps/Cryptodome/Cipher/_mode_openpgp.pyi | 20 - .../lib/deps/Cryptodome/Cipher/_mode_siv.py | 392 --- .../lib/deps/Cryptodome/Cipher/_mode_siv.pyi | 38 - .../Cryptodome/Cipher/_pkcs1_decode.abi3.so | Bin 56536 -> 0 bytes .../Cryptodome/Cipher/_pkcs1_oaep_decode.py | 41 - .../deps/Cryptodome/Cipher/_raw_aes.abi3.so | Bin 106808 -> 0 bytes .../deps/Cryptodome/Cipher/_raw_aesni.abi3.so | Bin 106384 -> 0 bytes .../deps/Cryptodome/Cipher/_raw_arc2.abi3.so | Bin 46464 -> 0 bytes .../Cryptodome/Cipher/_raw_blowfish.abi3.so | Bin 78640 -> 0 bytes .../deps/Cryptodome/Cipher/_raw_cast.abi3.so | Bin 57408 -> 0 bytes .../deps/Cryptodome/Cipher/_raw_cbc.abi3.so | Bin 23000 -> 0 bytes .../deps/Cryptodome/Cipher/_raw_cfb.abi3.so | Bin 26864 -> 0 bytes .../deps/Cryptodome/Cipher/_raw_ctr.abi3.so | Bin 31336 -> 0 bytes .../deps/Cryptodome/Cipher/_raw_des.abi3.so | Bin 71560 -> 0 bytes .../deps/Cryptodome/Cipher/_raw_des3.abi3.so | Bin 72520 -> 0 bytes .../deps/Cryptodome/Cipher/_raw_ecb.abi3.so | Bin 19016 -> 0 bytes .../Cipher/_raw_eksblowfish.abi3.so | Bin 181192 -> 0 bytes .../deps/Cryptodome/Cipher/_raw_ocb.abi3.so | Bin 45856 -> 0 bytes .../deps/Cryptodome/Cipher/_raw_ofb.abi3.so | Bin 22128 -> 0 bytes resources/lib/deps/Cryptodome/Hash/BLAKE2b.py | 247 -- .../lib/deps/Cryptodome/Hash/BLAKE2b.pyi | 32 - resources/lib/deps/Cryptodome/Hash/BLAKE2s.py | 247 -- .../lib/deps/Cryptodome/Hash/BLAKE2s.pyi | 26 - resources/lib/deps/Cryptodome/Hash/CMAC.py | 306 -- resources/lib/deps/Cryptodome/Hash/CMAC.pyi | 30 - resources/lib/deps/Cryptodome/Hash/HMAC.py | 238 -- resources/lib/deps/Cryptodome/Hash/HMAC.pyi | 25 - resources/lib/deps/Cryptodome/Hash/KMAC128.py | 179 -- .../lib/deps/Cryptodome/Hash/KMAC128.pyi | 33 - resources/lib/deps/Cryptodome/Hash/KMAC256.py | 74 - .../lib/deps/Cryptodome/Hash/KMAC256.pyi | 10 - .../deps/Cryptodome/Hash/KangarooTwelve.py | 222 -- .../deps/Cryptodome/Hash/KangarooTwelve.pyi | 16 - resources/lib/deps/Cryptodome/Hash/MD2.py | 166 - resources/lib/deps/Cryptodome/Hash/MD2.pyi | 19 - resources/lib/deps/Cryptodome/Hash/MD4.py | 185 -- resources/lib/deps/Cryptodome/Hash/MD4.pyi | 19 - resources/lib/deps/Cryptodome/Hash/MD5.py | 184 -- resources/lib/deps/Cryptodome/Hash/MD5.pyi | 19 - .../lib/deps/Cryptodome/Hash/Poly1305.py | 217 -- .../lib/deps/Cryptodome/Hash/Poly1305.pyi | 24 - resources/lib/deps/Cryptodome/Hash/RIPEMD.py | 26 - resources/lib/deps/Cryptodome/Hash/RIPEMD.pyi | 3 - .../lib/deps/Cryptodome/Hash/RIPEMD160.py | 169 - .../lib/deps/Cryptodome/Hash/RIPEMD160.pyi | 19 - resources/lib/deps/Cryptodome/Hash/SHA.py | 24 - resources/lib/deps/Cryptodome/Hash/SHA.pyi | 4 - resources/lib/deps/Cryptodome/Hash/SHA1.py | 185 -- resources/lib/deps/Cryptodome/Hash/SHA1.pyi | 19 - resources/lib/deps/Cryptodome/Hash/SHA224.py | 186 -- resources/lib/deps/Cryptodome/Hash/SHA224.pyi | 19 - resources/lib/deps/Cryptodome/Hash/SHA256.py | 185 -- resources/lib/deps/Cryptodome/Hash/SHA256.pyi | 18 - resources/lib/deps/Cryptodome/Hash/SHA384.py | 186 -- resources/lib/deps/Cryptodome/Hash/SHA384.pyi | 19 - .../lib/deps/Cryptodome/Hash/SHA3_224.py | 174 -- .../lib/deps/Cryptodome/Hash/SHA3_224.pyi | 19 - .../lib/deps/Cryptodome/Hash/SHA3_256.py | 174 -- .../lib/deps/Cryptodome/Hash/SHA3_256.pyi | 19 - .../lib/deps/Cryptodome/Hash/SHA3_384.py | 179 -- .../lib/deps/Cryptodome/Hash/SHA3_384.pyi | 19 - .../lib/deps/Cryptodome/Hash/SHA3_512.py | 174 -- .../lib/deps/Cryptodome/Hash/SHA3_512.pyi | 19 - resources/lib/deps/Cryptodome/Hash/SHA512.py | 204 -- resources/lib/deps/Cryptodome/Hash/SHA512.pyi | 22 - .../lib/deps/Cryptodome/Hash/SHAKE128.py | 129 - .../lib/deps/Cryptodome/Hash/SHAKE128.pyi | 13 - .../lib/deps/Cryptodome/Hash/SHAKE256.py | 130 - .../lib/deps/Cryptodome/Hash/SHAKE256.pyi | 13 - .../lib/deps/Cryptodome/Hash/TupleHash128.py | 136 - .../lib/deps/Cryptodome/Hash/TupleHash128.pyi | 22 - .../lib/deps/Cryptodome/Hash/TupleHash256.py | 70 - .../lib/deps/Cryptodome/Hash/TupleHash256.pyi | 5 - .../lib/deps/Cryptodome/Hash/TurboSHAKE128.py | 112 - .../deps/Cryptodome/Hash/TurboSHAKE128.pyi | 17 - .../lib/deps/Cryptodome/Hash/TurboSHAKE256.py | 22 - .../deps/Cryptodome/Hash/TurboSHAKE256.pyi | 12 - .../lib/deps/Cryptodome/Hash/_BLAKE2b.abi3.so | Bin 27424 -> 0 bytes .../lib/deps/Cryptodome/Hash/_BLAKE2s.abi3.so | Bin 26952 -> 0 bytes .../lib/deps/Cryptodome/Hash/_MD2.abi3.so | Bin 23136 -> 0 bytes .../lib/deps/Cryptodome/Hash/_MD4.abi3.so | Bin 27192 -> 0 bytes .../lib/deps/Cryptodome/Hash/_MD5.abi3.so | Bin 32008 -> 0 bytes .../deps/Cryptodome/Hash/_RIPEMD160.abi3.so | Bin 60728 -> 0 bytes .../lib/deps/Cryptodome/Hash/_SHA1.abi3.so | Bin 72064 -> 0 bytes .../lib/deps/Cryptodome/Hash/_SHA224.abi3.so | Bin 45768 -> 0 bytes .../lib/deps/Cryptodome/Hash/_SHA256.abi3.so | Bin 45832 -> 0 bytes .../lib/deps/Cryptodome/Hash/_SHA384.abi3.so | Bin 54160 -> 0 bytes .../lib/deps/Cryptodome/Hash/_SHA512.abi3.so | Bin 54200 -> 0 bytes .../lib/deps/Cryptodome/Hash/__init__.py | 69 - .../lib/deps/Cryptodome/Hash/__init__.pyi | 57 - .../deps/Cryptodome/Hash/_ghash_clmul.abi3.so | Bin 58376 -> 0 bytes .../Cryptodome/Hash/_ghash_portable.abi3.so | Bin 25024 -> 0 bytes .../lib/deps/Cryptodome/Hash/_keccak.abi3.so | Bin 41632 -> 0 bytes .../deps/Cryptodome/Hash/_poly1305.abi3.so | Bin 33424 -> 0 bytes .../lib/deps/Cryptodome/Hash/cSHAKE128.py | 187 -- .../lib/deps/Cryptodome/Hash/cSHAKE128.pyi | 14 - .../lib/deps/Cryptodome/Hash/cSHAKE256.py | 56 - .../lib/deps/Cryptodome/Hash/cSHAKE256.pyi | 8 - resources/lib/deps/Cryptodome/Hash/keccak.py | 181 -- resources/lib/deps/Cryptodome/Hash/keccak.pyi | 23 - resources/lib/deps/Cryptodome/IO/PEM.py | 189 -- resources/lib/deps/Cryptodome/IO/PEM.pyi | 10 - resources/lib/deps/Cryptodome/IO/PKCS8.py | 226 -- resources/lib/deps/Cryptodome/IO/PKCS8.pyi | 17 - resources/lib/deps/Cryptodome/IO/_PBES.py | 546 ---- resources/lib/deps/Cryptodome/IO/_PBES.pyi | 26 - resources/lib/deps/Cryptodome/IO/__init__.py | 31 - resources/lib/deps/Cryptodome/Math/Numbers.py | 42 - .../lib/deps/Cryptodome/Math/Numbers.pyi | 2 - .../lib/deps/Cryptodome/Math/Primality.py | 369 --- .../lib/deps/Cryptodome/Math/Primality.pyi | 18 - .../lib/deps/Cryptodome/Math/_IntegerBase.py | 412 --- .../lib/deps/Cryptodome/Math/_IntegerBase.pyi | 67 - .../deps/Cryptodome/Math/_IntegerCustom.py | 162 - .../deps/Cryptodome/Math/_IntegerCustom.pyi | 8 - .../lib/deps/Cryptodome/Math/_IntegerGMP.py | 782 ----- .../lib/deps/Cryptodome/Math/_IntegerGMP.pyi | 3 - .../deps/Cryptodome/Math/_IntegerNative.py | 382 --- .../deps/Cryptodome/Math/_IntegerNative.pyi | 3 - .../lib/deps/Cryptodome/Math/__init__.py | 0 .../lib/deps/Cryptodome/Math/_modexp.abi3.so | Bin 213552 -> 0 bytes resources/lib/deps/Cryptodome/Protocol/DH.py | 101 - resources/lib/deps/Cryptodome/Protocol/DH.pyi | 15 - resources/lib/deps/Cryptodome/Protocol/KDF.py | 642 ---- .../lib/deps/Cryptodome/Protocol/KDF.pyi | 42 - .../deps/Cryptodome/Protocol/SecretSharing.py | 278 -- .../Cryptodome/Protocol/SecretSharing.pyi | 22 - .../lib/deps/Cryptodome/Protocol/__init__.py | 31 - .../lib/deps/Cryptodome/Protocol/__init__.pyi | 1 - .../deps/Cryptodome/Protocol/_scrypt.abi3.so | Bin 26176 -> 0 bytes .../lib/deps/Cryptodome/PublicKey/DSA.py | 682 ---- .../lib/deps/Cryptodome/PublicKey/DSA.pyi | 31 - .../lib/deps/Cryptodome/PublicKey/ECC.py | 1818 ----------- .../lib/deps/Cryptodome/PublicKey/ECC.pyi | 102 - .../lib/deps/Cryptodome/PublicKey/ElGamal.py | 286 -- .../lib/deps/Cryptodome/PublicKey/ElGamal.pyi | 18 - .../lib/deps/Cryptodome/PublicKey/RSA.py | 864 ------ .../lib/deps/Cryptodome/PublicKey/RSA.pyi | 78 - .../lib/deps/Cryptodome/PublicKey/__init__.py | 94 - .../deps/Cryptodome/PublicKey/__init__.pyi | 0 .../deps/Cryptodome/PublicKey/_ec_ws.abi3.so | Bin 960552 -> 0 bytes .../Cryptodome/PublicKey/_ed25519.abi3.so | Bin 220544 -> 0 bytes .../deps/Cryptodome/PublicKey/_ed448.abi3.so | Bin 250592 -> 0 bytes .../lib/deps/Cryptodome/PublicKey/_openssh.py | 135 - .../deps/Cryptodome/PublicKey/_openssh.pyi | 7 - .../deps/Cryptodome/PublicKey/_x25519.abi3.so | Bin 79472 -> 0 bytes .../lib/deps/Cryptodome/Random/__init__.py | 57 - .../lib/deps/Cryptodome/Random/__init__.pyi | 19 - .../lib/deps/Cryptodome/Random/random.py | 138 - .../lib/deps/Cryptodome/Random/random.pyi | 22 - .../Cryptodome/SelfTest/Cipher/__init__.py | 60 - .../deps/Cryptodome/SelfTest/Cipher/common.py | 510 --- .../Cryptodome/SelfTest/Cipher/test_AES.py | 1351 -------- .../Cryptodome/SelfTest/Cipher/test_ARC2.py | 167 - .../Cryptodome/SelfTest/Cipher/test_ARC4.py | 471 --- .../SelfTest/Cipher/test_Blowfish.py | 160 - .../Cryptodome/SelfTest/Cipher/test_CAST.py | 101 - .../Cryptodome/SelfTest/Cipher/test_CBC.py | 556 ---- .../Cryptodome/SelfTest/Cipher/test_CCM.py | 936 ------ .../Cryptodome/SelfTest/Cipher/test_CFB.py | 411 --- .../Cryptodome/SelfTest/Cipher/test_CTR.py | 472 --- .../SelfTest/Cipher/test_ChaCha20.py | 529 ---- .../SelfTest/Cipher/test_ChaCha20_Poly1305.py | 776 ----- .../Cryptodome/SelfTest/Cipher/test_DES.py | 374 --- .../Cryptodome/SelfTest/Cipher/test_DES3.py | 195 -- .../Cryptodome/SelfTest/Cipher/test_EAX.py | 773 ----- .../Cryptodome/SelfTest/Cipher/test_GCM.py | 951 ------ .../Cryptodome/SelfTest/Cipher/test_OCB.py | 845 ----- .../Cryptodome/SelfTest/Cipher/test_OFB.py | 238 -- .../SelfTest/Cipher/test_OpenPGP.py | 218 -- .../Cryptodome/SelfTest/Cipher/test_SIV.py | 552 ---- .../SelfTest/Cipher/test_Salsa20.py | 367 --- .../SelfTest/Cipher/test_pkcs1_15.py | 283 -- .../SelfTest/Cipher/test_pkcs1_oaep.py | 506 --- .../deps/Cryptodome/SelfTest/Hash/__init__.py | 62 - .../deps/Cryptodome/SelfTest/Hash/common.py | 290 -- .../Cryptodome/SelfTest/Hash/test_BLAKE2.py | 482 --- .../Cryptodome/SelfTest/Hash/test_CMAC.py | 448 --- .../Cryptodome/SelfTest/Hash/test_HMAC.py | 548 ---- .../Cryptodome/SelfTest/Hash/test_KMAC.py | 346 --- .../SelfTest/Hash/test_KangarooTwelve.py | 367 --- .../deps/Cryptodome/SelfTest/Hash/test_MD2.py | 62 - .../deps/Cryptodome/SelfTest/Hash/test_MD4.py | 64 - .../deps/Cryptodome/SelfTest/Hash/test_MD5.py | 94 - .../Cryptodome/SelfTest/Hash/test_Poly1305.py | 542 ---- .../SelfTest/Hash/test_RIPEMD160.py | 71 - .../Cryptodome/SelfTest/Hash/test_SHA1.py | 84 - .../Cryptodome/SelfTest/Hash/test_SHA224.py | 63 - .../Cryptodome/SelfTest/Hash/test_SHA256.py | 94 - .../Cryptodome/SelfTest/Hash/test_SHA384.py | 61 - .../Cryptodome/SelfTest/Hash/test_SHA3_224.py | 79 - .../Cryptodome/SelfTest/Hash/test_SHA3_256.py | 80 - .../Cryptodome/SelfTest/Hash/test_SHA3_384.py | 79 - .../Cryptodome/SelfTest/Hash/test_SHA3_512.py | 79 - .../Cryptodome/SelfTest/Hash/test_SHA512.py | 140 - .../Cryptodome/SelfTest/Hash/test_SHAKE.py | 143 - .../SelfTest/Hash/test_TupleHash.py | 302 -- .../SelfTest/Hash/test_TurboSHAKE.py | 468 --- .../Cryptodome/SelfTest/Hash/test_cSHAKE.py | 178 -- .../Cryptodome/SelfTest/Hash/test_keccak.py | 250 -- .../deps/Cryptodome/SelfTest/IO/__init__.py | 47 - .../deps/Cryptodome/SelfTest/IO/test_PBES.py | 118 - .../deps/Cryptodome/SelfTest/IO/test_PKCS8.py | 459 --- .../deps/Cryptodome/SelfTest/Math/__init__.py | 51 - .../Cryptodome/SelfTest/Math/test_Numbers.py | 825 ----- .../SelfTest/Math/test_Primality.py | 118 - .../Cryptodome/SelfTest/Math/test_modexp.py | 201 -- .../Cryptodome/SelfTest/Math/test_modmult.py | 120 - .../Cryptodome/SelfTest/Protocol/__init__.py | 45 - .../Cryptodome/SelfTest/Protocol/test_KDF.py | 807 ----- .../SelfTest/Protocol/test_SecretSharing.py | 267 -- .../Cryptodome/SelfTest/Protocol/test_ecdh.py | 292 -- .../SelfTest/Protocol/test_rfc1751.py | 62 - .../Cryptodome/SelfTest/PublicKey/__init__.py | 53 - .../Cryptodome/SelfTest/PublicKey/test_DSA.py | 247 -- .../SelfTest/PublicKey/test_ECC_25519.py | 333 -- .../SelfTest/PublicKey/test_ECC_448.py | 333 -- .../SelfTest/PublicKey/test_ECC_NIST.py | 1425 --------- .../SelfTest/PublicKey/test_ElGamal.py | 217 -- .../Cryptodome/SelfTest/PublicKey/test_RSA.py | 324 -- .../SelfTest/PublicKey/test_import_DSA.py | 554 ---- .../SelfTest/PublicKey/test_import_ECC.py | 2763 ----------------- .../SelfTest/PublicKey/test_import_RSA.py | 629 ---- .../Cryptodome/SelfTest/Random/__init__.py | 39 - .../Cryptodome/SelfTest/Random/test_random.py | 167 - .../Cryptodome/SelfTest/Signature/__init__.py | 41 - .../Cryptodome/SelfTest/Signature/test_dss.py | 1369 -------- .../SelfTest/Signature/test_eddsa.py | 578 ---- .../SelfTest/Signature/test_pkcs1_15.py | 348 --- .../Cryptodome/SelfTest/Signature/test_pss.py | 377 --- .../deps/Cryptodome/SelfTest/Util/__init__.py | 46 - .../Cryptodome/SelfTest/Util/test_Counter.py | 67 - .../Cryptodome/SelfTest/Util/test_Padding.py | 154 - .../Cryptodome/SelfTest/Util/test_asn1.py | 851 ----- .../Cryptodome/SelfTest/Util/test_number.py | 192 -- .../Cryptodome/SelfTest/Util/test_rfc1751.py | 38 - .../Cryptodome/SelfTest/Util/test_strxor.py | 280 -- .../lib/deps/Cryptodome/SelfTest/__init__.py | 102 - .../lib/deps/Cryptodome/SelfTest/__main__.py | 43 - .../lib/deps/Cryptodome/SelfTest/loader.py | 239 -- .../lib/deps/Cryptodome/SelfTest/st_common.py | 55 - .../lib/deps/Cryptodome/Signature/DSS.py | 403 --- .../lib/deps/Cryptodome/Signature/DSS.pyi | 27 - .../deps/Cryptodome/Signature/PKCS1_PSS.py | 55 - .../deps/Cryptodome/Signature/PKCS1_PSS.pyi | 28 - .../deps/Cryptodome/Signature/PKCS1_v1_5.py | 53 - .../deps/Cryptodome/Signature/PKCS1_v1_5.pyi | 16 - .../lib/deps/Cryptodome/Signature/__init__.py | 36 - .../lib/deps/Cryptodome/Signature/eddsa.py | 343 -- .../lib/deps/Cryptodome/Signature/eddsa.pyi | 21 - .../lib/deps/Cryptodome/Signature/pkcs1_15.py | 223 -- .../deps/Cryptodome/Signature/pkcs1_15.pyi | 17 - .../lib/deps/Cryptodome/Signature/pss.py | 387 --- .../lib/deps/Cryptodome/Signature/pss.pyi | 30 - resources/lib/deps/Cryptodome/Util/Counter.py | 79 - .../lib/deps/Cryptodome/Util/Counter.pyi | 5 - resources/lib/deps/Cryptodome/Util/Padding.py | 108 - .../lib/deps/Cryptodome/Util/Padding.pyi | 6 - resources/lib/deps/Cryptodome/Util/RFC1751.py | 386 --- .../lib/deps/Cryptodome/Util/RFC1751.pyi | 7 - .../lib/deps/Cryptodome/Util/__init__.py | 41 - .../lib/deps/Cryptodome/Util/_cpu_features.py | 46 - .../deps/Cryptodome/Util/_cpu_features.pyi | 2 - .../lib/deps/Cryptodome/Util/_cpuid_c.abi3.so | Bin 19304 -> 0 bytes .../lib/deps/Cryptodome/Util/_file_system.py | 54 - .../lib/deps/Cryptodome/Util/_file_system.pyi | 4 - .../lib/deps/Cryptodome/Util/_raw_api.py | 325 -- .../lib/deps/Cryptodome/Util/_raw_api.pyi | 27 - .../lib/deps/Cryptodome/Util/_strxor.abi3.so | Bin 20376 -> 0 bytes resources/lib/deps/Cryptodome/Util/asn1.py | 1064 ------- resources/lib/deps/Cryptodome/Util/asn1.pyi | 80 - resources/lib/deps/Cryptodome/Util/number.py | 1525 --------- resources/lib/deps/Cryptodome/Util/number.pyi | 19 - .../lib/deps/Cryptodome/Util/py3compat.py | 185 -- .../lib/deps/Cryptodome/Util/py3compat.pyi | 33 - resources/lib/deps/Cryptodome/Util/strxor.py | 146 - resources/lib/deps/Cryptodome/Util/strxor.pyi | 6 - resources/lib/deps/Cryptodome/__init__.py | 6 - resources/lib/deps/Cryptodome/__init__.pyi | 4 - resources/lib/deps/Cryptodome/py.typed | 0 331 files changed, 1 insertion(+), 60797 deletions(-) delete mode 100644 resources/lib/deps/Cryptodome/Cipher/AES.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/AES.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/ARC2.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/ARC2.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/ARC4.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/ARC4.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/Blowfish.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/Blowfish.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/CAST.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/CAST.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/ChaCha20.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/ChaCha20.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/ChaCha20_Poly1305.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/ChaCha20_Poly1305.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/DES.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/DES.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/DES3.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/DES3.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/PKCS1_OAEP.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/PKCS1_OAEP.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/PKCS1_v1_5.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/PKCS1_v1_5.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/Salsa20.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/Salsa20.pyi delete mode 100755 resources/lib/deps/Cryptodome/Cipher/_ARC4.abi3.so delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_EKSBlowfish.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_EKSBlowfish.pyi delete mode 100755 resources/lib/deps/Cryptodome/Cipher/_Salsa20.abi3.so delete mode 100644 resources/lib/deps/Cryptodome/Cipher/__init__.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/__init__.pyi delete mode 100755 resources/lib/deps/Cryptodome/Cipher/_chacha20.abi3.so delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_cbc.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_cbc.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_ccm.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_ccm.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_cfb.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_cfb.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_ctr.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_ctr.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_eax.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_eax.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_ecb.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_ecb.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_gcm.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_gcm.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_ocb.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_ocb.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_ofb.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_ofb.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_openpgp.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_openpgp.pyi delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_siv.py delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_mode_siv.pyi delete mode 100755 resources/lib/deps/Cryptodome/Cipher/_pkcs1_decode.abi3.so delete mode 100644 resources/lib/deps/Cryptodome/Cipher/_pkcs1_oaep_decode.py delete mode 100755 resources/lib/deps/Cryptodome/Cipher/_raw_aes.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Cipher/_raw_aesni.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Cipher/_raw_arc2.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Cipher/_raw_blowfish.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Cipher/_raw_cast.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Cipher/_raw_cbc.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Cipher/_raw_cfb.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Cipher/_raw_ctr.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Cipher/_raw_des.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Cipher/_raw_des3.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Cipher/_raw_ecb.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Cipher/_raw_eksblowfish.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Cipher/_raw_ocb.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Cipher/_raw_ofb.abi3.so delete mode 100644 resources/lib/deps/Cryptodome/Hash/BLAKE2b.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/BLAKE2b.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/BLAKE2s.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/BLAKE2s.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/CMAC.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/CMAC.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/HMAC.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/HMAC.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/KMAC128.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/KMAC128.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/KMAC256.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/KMAC256.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/KangarooTwelve.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/KangarooTwelve.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/MD2.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/MD2.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/MD4.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/MD4.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/MD5.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/MD5.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/Poly1305.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/Poly1305.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/RIPEMD.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/RIPEMD.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/RIPEMD160.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/RIPEMD160.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA1.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA1.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA224.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA224.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA256.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA256.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA384.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA384.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA3_224.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA3_224.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA3_256.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA3_256.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA3_384.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA3_384.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA3_512.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA3_512.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA512.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHA512.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHAKE128.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHAKE128.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHAKE256.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/SHAKE256.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/TupleHash128.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/TupleHash128.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/TupleHash256.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/TupleHash256.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/TurboSHAKE128.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/TurboSHAKE128.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/TurboSHAKE256.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/TurboSHAKE256.pyi delete mode 100755 resources/lib/deps/Cryptodome/Hash/_BLAKE2b.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Hash/_BLAKE2s.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Hash/_MD2.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Hash/_MD4.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Hash/_MD5.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Hash/_RIPEMD160.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Hash/_SHA1.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Hash/_SHA224.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Hash/_SHA256.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Hash/_SHA384.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Hash/_SHA512.abi3.so delete mode 100644 resources/lib/deps/Cryptodome/Hash/__init__.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/__init__.pyi delete mode 100755 resources/lib/deps/Cryptodome/Hash/_ghash_clmul.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Hash/_ghash_portable.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Hash/_keccak.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/Hash/_poly1305.abi3.so delete mode 100644 resources/lib/deps/Cryptodome/Hash/cSHAKE128.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/cSHAKE128.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/cSHAKE256.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/cSHAKE256.pyi delete mode 100644 resources/lib/deps/Cryptodome/Hash/keccak.py delete mode 100644 resources/lib/deps/Cryptodome/Hash/keccak.pyi delete mode 100644 resources/lib/deps/Cryptodome/IO/PEM.py delete mode 100644 resources/lib/deps/Cryptodome/IO/PEM.pyi delete mode 100644 resources/lib/deps/Cryptodome/IO/PKCS8.py delete mode 100644 resources/lib/deps/Cryptodome/IO/PKCS8.pyi delete mode 100644 resources/lib/deps/Cryptodome/IO/_PBES.py delete mode 100644 resources/lib/deps/Cryptodome/IO/_PBES.pyi delete mode 100644 resources/lib/deps/Cryptodome/IO/__init__.py delete mode 100644 resources/lib/deps/Cryptodome/Math/Numbers.py delete mode 100644 resources/lib/deps/Cryptodome/Math/Numbers.pyi delete mode 100644 resources/lib/deps/Cryptodome/Math/Primality.py delete mode 100644 resources/lib/deps/Cryptodome/Math/Primality.pyi delete mode 100644 resources/lib/deps/Cryptodome/Math/_IntegerBase.py delete mode 100644 resources/lib/deps/Cryptodome/Math/_IntegerBase.pyi delete mode 100644 resources/lib/deps/Cryptodome/Math/_IntegerCustom.py delete mode 100644 resources/lib/deps/Cryptodome/Math/_IntegerCustom.pyi delete mode 100644 resources/lib/deps/Cryptodome/Math/_IntegerGMP.py delete mode 100644 resources/lib/deps/Cryptodome/Math/_IntegerGMP.pyi delete mode 100644 resources/lib/deps/Cryptodome/Math/_IntegerNative.py delete mode 100644 resources/lib/deps/Cryptodome/Math/_IntegerNative.pyi delete mode 100644 resources/lib/deps/Cryptodome/Math/__init__.py delete mode 100755 resources/lib/deps/Cryptodome/Math/_modexp.abi3.so delete mode 100644 resources/lib/deps/Cryptodome/Protocol/DH.py delete mode 100644 resources/lib/deps/Cryptodome/Protocol/DH.pyi delete mode 100644 resources/lib/deps/Cryptodome/Protocol/KDF.py delete mode 100644 resources/lib/deps/Cryptodome/Protocol/KDF.pyi delete mode 100644 resources/lib/deps/Cryptodome/Protocol/SecretSharing.py delete mode 100644 resources/lib/deps/Cryptodome/Protocol/SecretSharing.pyi delete mode 100644 resources/lib/deps/Cryptodome/Protocol/__init__.py delete mode 100644 resources/lib/deps/Cryptodome/Protocol/__init__.pyi delete mode 100755 resources/lib/deps/Cryptodome/Protocol/_scrypt.abi3.so delete mode 100644 resources/lib/deps/Cryptodome/PublicKey/DSA.py delete mode 100644 resources/lib/deps/Cryptodome/PublicKey/DSA.pyi delete mode 100644 resources/lib/deps/Cryptodome/PublicKey/ECC.py delete mode 100644 resources/lib/deps/Cryptodome/PublicKey/ECC.pyi delete mode 100644 resources/lib/deps/Cryptodome/PublicKey/ElGamal.py delete mode 100644 resources/lib/deps/Cryptodome/PublicKey/ElGamal.pyi delete mode 100644 resources/lib/deps/Cryptodome/PublicKey/RSA.py delete mode 100644 resources/lib/deps/Cryptodome/PublicKey/RSA.pyi delete mode 100644 resources/lib/deps/Cryptodome/PublicKey/__init__.py delete mode 100644 resources/lib/deps/Cryptodome/PublicKey/__init__.pyi delete mode 100755 resources/lib/deps/Cryptodome/PublicKey/_ec_ws.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/PublicKey/_ed25519.abi3.so delete mode 100755 resources/lib/deps/Cryptodome/PublicKey/_ed448.abi3.so delete mode 100644 resources/lib/deps/Cryptodome/PublicKey/_openssh.py delete mode 100644 resources/lib/deps/Cryptodome/PublicKey/_openssh.pyi delete mode 100755 resources/lib/deps/Cryptodome/PublicKey/_x25519.abi3.so delete mode 100644 resources/lib/deps/Cryptodome/Random/__init__.py delete mode 100644 resources/lib/deps/Cryptodome/Random/__init__.pyi delete mode 100644 resources/lib/deps/Cryptodome/Random/random.py delete mode 100644 resources/lib/deps/Cryptodome/Random/random.pyi delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/__init__.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/common.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_AES.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_ARC2.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_ARC4.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_Blowfish.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CAST.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CBC.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CCM.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CFB.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CTR.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_ChaCha20.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_ChaCha20_Poly1305.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_DES.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_DES3.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_EAX.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_GCM.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_OCB.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_OFB.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_OpenPGP.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_SIV.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_Salsa20.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_pkcs1_15.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Cipher/test_pkcs1_oaep.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/__init__.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/common.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_BLAKE2.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_CMAC.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_HMAC.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_KMAC.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_KangarooTwelve.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_MD2.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_MD4.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_MD5.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_Poly1305.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_RIPEMD160.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA1.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA224.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA256.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA384.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA3_224.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA3_256.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA3_384.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA3_512.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA512.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHAKE.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_TupleHash.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_TurboSHAKE.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_cSHAKE.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Hash/test_keccak.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/IO/__init__.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/IO/test_PBES.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/IO/test_PKCS8.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Math/__init__.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Math/test_Numbers.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Math/test_Primality.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Math/test_modexp.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Math/test_modmult.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Protocol/__init__.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Protocol/test_KDF.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Protocol/test_SecretSharing.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Protocol/test_ecdh.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Protocol/test_rfc1751.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/PublicKey/__init__.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_DSA.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_ECC_25519.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_ECC_448.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_ECC_NIST.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_ElGamal.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_RSA.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_import_DSA.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_import_ECC.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_import_RSA.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Random/__init__.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Random/test_random.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Signature/__init__.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Signature/test_dss.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Signature/test_eddsa.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Signature/test_pkcs1_15.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Signature/test_pss.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Util/__init__.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Util/test_Counter.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Util/test_Padding.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Util/test_asn1.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Util/test_number.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Util/test_rfc1751.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/Util/test_strxor.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/__init__.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/__main__.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/loader.py delete mode 100644 resources/lib/deps/Cryptodome/SelfTest/st_common.py delete mode 100644 resources/lib/deps/Cryptodome/Signature/DSS.py delete mode 100644 resources/lib/deps/Cryptodome/Signature/DSS.pyi delete mode 100644 resources/lib/deps/Cryptodome/Signature/PKCS1_PSS.py delete mode 100644 resources/lib/deps/Cryptodome/Signature/PKCS1_PSS.pyi delete mode 100644 resources/lib/deps/Cryptodome/Signature/PKCS1_v1_5.py delete mode 100644 resources/lib/deps/Cryptodome/Signature/PKCS1_v1_5.pyi delete mode 100644 resources/lib/deps/Cryptodome/Signature/__init__.py delete mode 100644 resources/lib/deps/Cryptodome/Signature/eddsa.py delete mode 100644 resources/lib/deps/Cryptodome/Signature/eddsa.pyi delete mode 100644 resources/lib/deps/Cryptodome/Signature/pkcs1_15.py delete mode 100644 resources/lib/deps/Cryptodome/Signature/pkcs1_15.pyi delete mode 100644 resources/lib/deps/Cryptodome/Signature/pss.py delete mode 100644 resources/lib/deps/Cryptodome/Signature/pss.pyi delete mode 100644 resources/lib/deps/Cryptodome/Util/Counter.py delete mode 100644 resources/lib/deps/Cryptodome/Util/Counter.pyi delete mode 100644 resources/lib/deps/Cryptodome/Util/Padding.py delete mode 100644 resources/lib/deps/Cryptodome/Util/Padding.pyi delete mode 100644 resources/lib/deps/Cryptodome/Util/RFC1751.py delete mode 100644 resources/lib/deps/Cryptodome/Util/RFC1751.pyi delete mode 100644 resources/lib/deps/Cryptodome/Util/__init__.py delete mode 100644 resources/lib/deps/Cryptodome/Util/_cpu_features.py delete mode 100644 resources/lib/deps/Cryptodome/Util/_cpu_features.pyi delete mode 100755 resources/lib/deps/Cryptodome/Util/_cpuid_c.abi3.so delete mode 100644 resources/lib/deps/Cryptodome/Util/_file_system.py delete mode 100644 resources/lib/deps/Cryptodome/Util/_file_system.pyi delete mode 100644 resources/lib/deps/Cryptodome/Util/_raw_api.py delete mode 100644 resources/lib/deps/Cryptodome/Util/_raw_api.pyi delete mode 100755 resources/lib/deps/Cryptodome/Util/_strxor.abi3.so delete mode 100644 resources/lib/deps/Cryptodome/Util/asn1.py delete mode 100644 resources/lib/deps/Cryptodome/Util/asn1.pyi delete mode 100644 resources/lib/deps/Cryptodome/Util/number.py delete mode 100644 resources/lib/deps/Cryptodome/Util/number.pyi delete mode 100644 resources/lib/deps/Cryptodome/Util/py3compat.py delete mode 100644 resources/lib/deps/Cryptodome/Util/py3compat.pyi delete mode 100644 resources/lib/deps/Cryptodome/Util/strxor.py delete mode 100644 resources/lib/deps/Cryptodome/Util/strxor.pyi delete mode 100644 resources/lib/deps/Cryptodome/__init__.py delete mode 100644 resources/lib/deps/Cryptodome/__init__.pyi delete mode 100644 resources/lib/deps/Cryptodome/py.typed diff --git a/addon.xml b/addon.xml index b37dab0..75b49a3 100644 --- a/addon.xml +++ b/addon.xml @@ -10,6 +10,7 @@ + diff --git a/resources/lib/deps/Cryptodome/Cipher/AES.py b/resources/lib/deps/Cryptodome/Cipher/AES.py deleted file mode 100644 index 402a3d7..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/AES.py +++ /dev/null @@ -1,234 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Cipher/AES.py : AES -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -import sys - -from Cryptodome.Cipher import _create_cipher -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - c_size_t, c_uint8_ptr) - -from Cryptodome.Util import _cpu_features -from Cryptodome.Random import get_random_bytes - -MODE_ECB = 1 #: Electronic Code Book (:ref:`ecb_mode`) -MODE_CBC = 2 #: Cipher-Block Chaining (:ref:`cbc_mode`) -MODE_CFB = 3 #: Cipher Feedback (:ref:`cfb_mode`) -MODE_OFB = 5 #: Output Feedback (:ref:`ofb_mode`) -MODE_CTR = 6 #: Counter mode (:ref:`ctr_mode`) -MODE_OPENPGP = 7 #: OpenPGP mode (:ref:`openpgp_mode`) -MODE_CCM = 8 #: Counter with CBC-MAC (:ref:`ccm_mode`) -MODE_EAX = 9 #: :ref:`eax_mode` -MODE_SIV = 10 #: Synthetic Initialization Vector (:ref:`siv_mode`) -MODE_GCM = 11 #: Galois Counter Mode (:ref:`gcm_mode`) -MODE_OCB = 12 #: Offset Code Book (:ref:`ocb_mode`) - - -_cproto = """ - int AES_start_operation(const uint8_t key[], - size_t key_len, - void **pResult); - int AES_encrypt(const void *state, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int AES_decrypt(const void *state, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int AES_stop_operation(void *state); - """ - - -# Load portable AES -_raw_aes_lib = load_pycryptodome_raw_lib("Cryptodome.Cipher._raw_aes", - _cproto) - -# Try to load AES with AES NI instructions -try: - _raw_aesni_lib = None - if _cpu_features.have_aes_ni(): - _raw_aesni_lib = load_pycryptodome_raw_lib("Cryptodome.Cipher._raw_aesni", - _cproto.replace("AES", - "AESNI")) -# _raw_aesni may not have been compiled in -except OSError: - pass - - -def _create_base_cipher(dict_parameters): - """This method instantiates and returns a handle to a low-level - base cipher. It will absorb named parameters in the process.""" - - use_aesni = dict_parameters.pop("use_aesni", True) - - try: - key = dict_parameters.pop("key") - except KeyError: - raise TypeError("Missing 'key' parameter") - - if len(key) not in key_size: - raise ValueError("Incorrect AES key length (%d bytes)" % len(key)) - - if use_aesni and _raw_aesni_lib: - start_operation = _raw_aesni_lib.AESNI_start_operation - stop_operation = _raw_aesni_lib.AESNI_stop_operation - else: - start_operation = _raw_aes_lib.AES_start_operation - stop_operation = _raw_aes_lib.AES_stop_operation - - cipher = VoidPointer() - result = start_operation(c_uint8_ptr(key), - c_size_t(len(key)), - cipher.address_of()) - if result: - raise ValueError("Error %X while instantiating the AES cipher" - % result) - return SmartPointer(cipher.get(), stop_operation) - - -def _derive_Poly1305_key_pair(key, nonce): - """Derive a tuple (r, s, nonce) for a Poly1305 MAC. - - If nonce is ``None``, a new 16-byte nonce is generated. - """ - - if len(key) != 32: - raise ValueError("Poly1305 with AES requires a 32-byte key") - - if nonce is None: - nonce = get_random_bytes(16) - elif len(nonce) != 16: - raise ValueError("Poly1305 with AES requires a 16-byte nonce") - - s = new(key[:16], MODE_ECB).encrypt(nonce) - return key[16:], s, nonce - - -def new(key, mode, *args, **kwargs): - """Create a new AES cipher. - - Args: - key(bytes/bytearray/memoryview): - The secret key to use in the symmetric cipher. - - It must be 16 (*AES-128)*, 24 (*AES-192*) or 32 (*AES-256*) bytes long. - - For ``MODE_SIV`` only, it doubles to 32, 48, or 64 bytes. - mode (a ``MODE_*`` constant): - The chaining mode to use for encryption or decryption. - If in doubt, use ``MODE_EAX``. - - Keyword Args: - iv (bytes/bytearray/memoryview): - (Only applicable for ``MODE_CBC``, ``MODE_CFB``, ``MODE_OFB``, - and ``MODE_OPENPGP`` modes). - - The initialization vector to use for encryption or decryption. - - For ``MODE_CBC``, ``MODE_CFB``, and ``MODE_OFB`` it must be 16 bytes long. - - For ``MODE_OPENPGP`` mode only, - it must be 16 bytes long for encryption - and 18 bytes for decryption (in the latter case, it is - actually the *encrypted* IV which was prefixed to the ciphertext). - - If not provided, a random byte string is generated (you must then - read its value with the :attr:`iv` attribute). - - nonce (bytes/bytearray/memoryview): - (Only applicable for ``MODE_CCM``, ``MODE_EAX``, ``MODE_GCM``, - ``MODE_SIV``, ``MODE_OCB``, and ``MODE_CTR``). - - A value that must never be reused for any other encryption done - with this key (except possibly for ``MODE_SIV``, see below). - - For ``MODE_EAX``, ``MODE_GCM`` and ``MODE_SIV`` there are no - restrictions on its length (recommended: **16** bytes). - - For ``MODE_CCM``, its length must be in the range **[7..13]**. - Bear in mind that with CCM there is a trade-off between nonce - length and maximum message size. Recommendation: **11** bytes. - - For ``MODE_OCB``, its length must be in the range **[1..15]** - (recommended: **15**). - - For ``MODE_CTR``, its length must be in the range **[0..15]** - (recommended: **8**). - - For ``MODE_SIV``, the nonce is optional, if it is not specified, - then no nonce is being used, which renders the encryption - deterministic. - - If not provided, for modes other than ``MODE_SIV``, a random - byte string of the recommended length is used (you must then - read its value with the :attr:`nonce` attribute). - - segment_size (integer): - (Only ``MODE_CFB``).The number of **bits** the plaintext and ciphertext - are segmented in. It must be a multiple of 8. - If not specified, it will be assumed to be 8. - - mac_len (integer): - (Only ``MODE_EAX``, ``MODE_GCM``, ``MODE_OCB``, ``MODE_CCM``) - Length of the authentication tag, in bytes. - - It must be even and in the range **[4..16]**. - The recommended value (and the default, if not specified) is **16**. - - msg_len (integer): - (Only ``MODE_CCM``). Length of the message to (de)cipher. - If not specified, ``encrypt`` must be called with the entire message. - Similarly, ``decrypt`` can only be called once. - - assoc_len (integer): - (Only ``MODE_CCM``). Length of the associated data. - If not specified, all associated data is buffered internally, - which may represent a problem for very large messages. - - initial_value (integer or bytes/bytearray/memoryview): - (Only ``MODE_CTR``). - The initial value for the counter. If not present, the cipher will - start counting from 0. The value is incremented by one for each block. - The counter number is encoded in big endian mode. - - counter (object): - (Only ``MODE_CTR``). - Instance of ``Cryptodome.Util.Counter``, which allows full customization - of the counter block. This parameter is incompatible to both ``nonce`` - and ``initial_value``. - - use_aesni: (boolean): - Use Intel AES-NI hardware extensions (default: use if available). - - Returns: - an AES object, of the applicable mode. - """ - - kwargs["add_aes_modes"] = True - return _create_cipher(sys.modules[__name__], key, mode, *args, **kwargs) - - -# Size of a data block (in bytes) -block_size = 16 -# Size of a key (in bytes) -key_size = (16, 24, 32) diff --git a/resources/lib/deps/Cryptodome/Cipher/AES.pyi b/resources/lib/deps/Cryptodome/Cipher/AES.pyi deleted file mode 100644 index 3f07b65..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/AES.pyi +++ /dev/null @@ -1,156 +0,0 @@ -from typing import Dict, Optional, Tuple, Union, overload -from typing_extensions import Literal - -Buffer=bytes|bytearray|memoryview - -from Cryptodome.Cipher._mode_ecb import EcbMode -from Cryptodome.Cipher._mode_cbc import CbcMode -from Cryptodome.Cipher._mode_cfb import CfbMode -from Cryptodome.Cipher._mode_ofb import OfbMode -from Cryptodome.Cipher._mode_ctr import CtrMode -from Cryptodome.Cipher._mode_openpgp import OpenPgpMode -from Cryptodome.Cipher._mode_ccm import CcmMode -from Cryptodome.Cipher._mode_eax import EaxMode -from Cryptodome.Cipher._mode_gcm import GcmMode -from Cryptodome.Cipher._mode_siv import SivMode -from Cryptodome.Cipher._mode_ocb import OcbMode - -MODE_ECB: Literal[1] -MODE_CBC: Literal[2] -MODE_CFB: Literal[3] -MODE_OFB: Literal[5] -MODE_CTR: Literal[6] -MODE_OPENPGP: Literal[7] -MODE_CCM: Literal[8] -MODE_EAX: Literal[9] -MODE_SIV: Literal[10] -MODE_GCM: Literal[11] -MODE_OCB: Literal[12] - -# MODE_ECB -@overload -def new(key: Buffer, - mode: Literal[1], - use_aesni : bool = ...) -> \ - EcbMode: ... - -# MODE_CBC -@overload -def new(key: Buffer, - mode: Literal[2], - iv : Optional[Buffer] = ..., - use_aesni : bool = ...) -> \ - CbcMode: ... - -@overload -def new(key: Buffer, - mode: Literal[2], - IV : Optional[Buffer] = ..., - use_aesni : bool = ...) -> \ - CbcMode: ... - -# MODE_CFB -@overload -def new(key: Buffer, - mode: Literal[3], - iv : Optional[Buffer] = ..., - segment_size : int = ..., - use_aesni : bool = ...) -> \ - CfbMode: ... - -@overload -def new(key: Buffer, - mode: Literal[3], - IV : Optional[Buffer] = ..., - segment_size : int = ..., - use_aesni : bool = ...) -> \ - CfbMode: ... - -# MODE_OFB -@overload -def new(key: Buffer, - mode: Literal[5], - iv : Optional[Buffer] = ..., - use_aesni : bool = ...) -> \ - OfbMode: ... - -@overload -def new(key: Buffer, - mode: Literal[5], - IV : Optional[Buffer] = ..., - use_aesni : bool = ...) -> \ - OfbMode: ... - -# MODE_CTR -@overload -def new(key: Buffer, - mode: Literal[6], - nonce : Optional[Buffer] = ..., - initial_value : Union[int, Buffer] = ..., - counter : Dict = ..., - use_aesni : bool = ...) -> \ - CtrMode: ... - -# MODE_OPENPGP -@overload -def new(key: Buffer, - mode: Literal[7], - iv : Optional[Buffer] = ..., - use_aesni : bool = ...) -> \ - OpenPgpMode: ... - -@overload -def new(key: Buffer, - mode: Literal[7], - IV : Optional[Buffer] = ..., - use_aesni : bool = ...) -> \ - OpenPgpMode: ... - -# MODE_CCM -@overload -def new(key: Buffer, - mode: Literal[8], - nonce : Optional[Buffer] = ..., - mac_len : int = ..., - assoc_len : int = ..., - use_aesni : bool = ...) -> \ - CcmMode: ... - -# MODE_EAX -@overload -def new(key: Buffer, - mode: Literal[9], - nonce : Optional[Buffer] = ..., - mac_len : int = ..., - use_aesni : bool = ...) -> \ - EaxMode: ... - -# MODE_GCM -@overload -def new(key: Buffer, - mode: Literal[10], - nonce : Optional[Buffer] = ..., - use_aesni : bool = ...) -> \ - SivMode: ... - -# MODE_SIV -@overload -def new(key: Buffer, - mode: Literal[11], - nonce : Optional[Buffer] = ..., - mac_len : int = ..., - use_aesni : bool = ...) -> \ - GcmMode: ... - -# MODE_OCB -@overload -def new(key: Buffer, - mode: Literal[12], - nonce : Optional[Buffer] = ..., - mac_len : int = ..., - use_aesni : bool = ...) -> \ - OcbMode: ... - - -block_size: int -key_size: Tuple[int, int, int] diff --git a/resources/lib/deps/Cryptodome/Cipher/ARC2.py b/resources/lib/deps/Cryptodome/Cipher/ARC2.py deleted file mode 100644 index 4dc1bb8..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/ARC2.py +++ /dev/null @@ -1,175 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Cipher/ARC2.py : ARC2.py -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== -""" -Module's constants for the modes of operation supported with ARC2: - -:var MODE_ECB: :ref:`Electronic Code Book (ECB) ` -:var MODE_CBC: :ref:`Cipher-Block Chaining (CBC) ` -:var MODE_CFB: :ref:`Cipher FeedBack (CFB) ` -:var MODE_OFB: :ref:`Output FeedBack (OFB) ` -:var MODE_CTR: :ref:`CounTer Mode (CTR) ` -:var MODE_OPENPGP: :ref:`OpenPGP Mode ` -:var MODE_EAX: :ref:`EAX Mode ` -""" - -import sys - -from Cryptodome.Cipher import _create_cipher -from Cryptodome.Util.py3compat import byte_string -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - c_size_t, c_uint8_ptr) - -_raw_arc2_lib = load_pycryptodome_raw_lib( - "Cryptodome.Cipher._raw_arc2", - """ - int ARC2_start_operation(const uint8_t key[], - size_t key_len, - size_t effective_key_len, - void **pResult); - int ARC2_encrypt(const void *state, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int ARC2_decrypt(const void *state, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int ARC2_stop_operation(void *state); - """ - ) - - -def _create_base_cipher(dict_parameters): - """This method instantiates and returns a handle to a low-level - base cipher. It will absorb named parameters in the process.""" - - try: - key = dict_parameters.pop("key") - except KeyError: - raise TypeError("Missing 'key' parameter") - - effective_keylen = dict_parameters.pop("effective_keylen", 1024) - - if len(key) not in key_size: - raise ValueError("Incorrect ARC2 key length (%d bytes)" % len(key)) - - if not (40 <= effective_keylen <= 1024): - raise ValueError("'effective_key_len' must be at least 40 and no larger than 1024 " - "(not %d)" % effective_keylen) - - start_operation = _raw_arc2_lib.ARC2_start_operation - stop_operation = _raw_arc2_lib.ARC2_stop_operation - - cipher = VoidPointer() - result = start_operation(c_uint8_ptr(key), - c_size_t(len(key)), - c_size_t(effective_keylen), - cipher.address_of()) - if result: - raise ValueError("Error %X while instantiating the ARC2 cipher" - % result) - - return SmartPointer(cipher.get(), stop_operation) - - -def new(key, mode, *args, **kwargs): - """Create a new RC2 cipher. - - :param key: - The secret key to use in the symmetric cipher. - Its length can vary from 5 to 128 bytes; the actual search space - (and the cipher strength) can be reduced with the ``effective_keylen`` parameter. - :type key: bytes, bytearray, memoryview - - :param mode: - The chaining mode to use for encryption or decryption. - :type mode: One of the supported ``MODE_*`` constants - - :Keyword Arguments: - * **iv** (*bytes*, *bytearray*, *memoryview*) -- - (Only applicable for ``MODE_CBC``, ``MODE_CFB``, ``MODE_OFB``, - and ``MODE_OPENPGP`` modes). - - The initialization vector to use for encryption or decryption. - - For ``MODE_CBC``, ``MODE_CFB``, and ``MODE_OFB`` it must be 8 bytes long. - - For ``MODE_OPENPGP`` mode only, - it must be 8 bytes long for encryption - and 10 bytes for decryption (in the latter case, it is - actually the *encrypted* IV which was prefixed to the ciphertext). - - If not provided, a random byte string is generated (you must then - read its value with the :attr:`iv` attribute). - - * **nonce** (*bytes*, *bytearray*, *memoryview*) -- - (Only applicable for ``MODE_EAX`` and ``MODE_CTR``). - - A value that must never be reused for any other encryption done - with this key. - - For ``MODE_EAX`` there are no - restrictions on its length (recommended: **16** bytes). - - For ``MODE_CTR``, its length must be in the range **[0..7]**. - - If not provided for ``MODE_EAX``, a random byte string is generated (you - can read it back via the ``nonce`` attribute). - - * **effective_keylen** (*integer*) -- - Optional. Maximum strength in bits of the actual key used by the ARC2 algorithm. - If the supplied ``key`` parameter is longer (in bits) of the value specified - here, it will be weakened to match it. - If not specified, no limitation is applied. - - * **segment_size** (*integer*) -- - (Only ``MODE_CFB``).The number of **bits** the plaintext and ciphertext - are segmented in. It must be a multiple of 8. - If not specified, it will be assumed to be 8. - - * **mac_len** : (*integer*) -- - (Only ``MODE_EAX``) - Length of the authentication tag, in bytes. - It must be no longer than 8 (default). - - * **initial_value** : (*integer*) -- - (Only ``MODE_CTR``). The initial value for the counter within - the counter block. By default it is **0**. - - :Return: an ARC2 object, of the applicable mode. - """ - - return _create_cipher(sys.modules[__name__], key, mode, *args, **kwargs) - -MODE_ECB = 1 -MODE_CBC = 2 -MODE_CFB = 3 -MODE_OFB = 5 -MODE_CTR = 6 -MODE_OPENPGP = 7 -MODE_EAX = 9 - -# Size of a data block (in bytes) -block_size = 8 -# Size of a key (in bytes) -key_size = range(5, 128 + 1) diff --git a/resources/lib/deps/Cryptodome/Cipher/ARC2.pyi b/resources/lib/deps/Cryptodome/Cipher/ARC2.pyi deleted file mode 100644 index a122a52..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/ARC2.pyi +++ /dev/null @@ -1,35 +0,0 @@ -from typing import Union, Dict, Iterable, Optional - -Buffer = bytes|bytearray|memoryview - -from Cryptodome.Cipher._mode_ecb import EcbMode -from Cryptodome.Cipher._mode_cbc import CbcMode -from Cryptodome.Cipher._mode_cfb import CfbMode -from Cryptodome.Cipher._mode_ofb import OfbMode -from Cryptodome.Cipher._mode_ctr import CtrMode -from Cryptodome.Cipher._mode_openpgp import OpenPgpMode -from Cryptodome.Cipher._mode_eax import EaxMode - -ARC2Mode = int - -MODE_ECB: ARC2Mode -MODE_CBC: ARC2Mode -MODE_CFB: ARC2Mode -MODE_OFB: ARC2Mode -MODE_CTR: ARC2Mode -MODE_OPENPGP: ARC2Mode -MODE_EAX: ARC2Mode - -def new(key: Buffer, - mode: ARC2Mode, - iv : Optional[Buffer] = ..., - IV : Optional[Buffer] = ..., - nonce : Optional[Buffer] = ..., - segment_size : int = ..., - mac_len : int = ..., - initial_value : Union[int, Buffer] = ..., - counter : Dict = ...) -> \ - Union[EcbMode, CbcMode, CfbMode, OfbMode, CtrMode, OpenPgpMode]: ... - -block_size: int -key_size: Iterable[int] diff --git a/resources/lib/deps/Cryptodome/Cipher/ARC4.py b/resources/lib/deps/Cryptodome/Cipher/ARC4.py deleted file mode 100644 index 543a323..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/ARC4.py +++ /dev/null @@ -1,136 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Cipher/ARC4.py : ARC4 -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, - create_string_buffer, get_raw_buffer, - SmartPointer, c_size_t, c_uint8_ptr) - - -_raw_arc4_lib = load_pycryptodome_raw_lib("Cryptodome.Cipher._ARC4", """ - int ARC4_stream_encrypt(void *rc4State, const uint8_t in[], - uint8_t out[], size_t len); - int ARC4_stream_init(uint8_t *key, size_t keylen, - void **pRc4State); - int ARC4_stream_destroy(void *rc4State); - """) - - -class ARC4Cipher: - """ARC4 cipher object. Do not create it directly. Use - :func:`Cryptodome.Cipher.ARC4.new` instead. - """ - - def __init__(self, key, *args, **kwargs): - """Initialize an ARC4 cipher object - - See also `new()` at the module level.""" - - if len(args) > 0: - ndrop = args[0] - args = args[1:] - else: - ndrop = kwargs.pop('drop', 0) - - if len(key) not in key_size: - raise ValueError("Incorrect ARC4 key length (%d bytes)" % - len(key)) - - self._state = VoidPointer() - result = _raw_arc4_lib.ARC4_stream_init(c_uint8_ptr(key), - c_size_t(len(key)), - self._state.address_of()) - if result != 0: - raise ValueError("Error %d while creating the ARC4 cipher" - % result) - self._state = SmartPointer(self._state.get(), - _raw_arc4_lib.ARC4_stream_destroy) - - if ndrop > 0: - # This is OK even if the cipher is used for decryption, - # since encrypt and decrypt are actually the same thing - # with ARC4. - self.encrypt(b'\x00' * ndrop) - - self.block_size = 1 - self.key_size = len(key) - - def encrypt(self, plaintext): - """Encrypt a piece of data. - - :param plaintext: The data to encrypt, of any size. - :type plaintext: bytes, bytearray, memoryview - :returns: the encrypted byte string, of equal length as the - plaintext. - """ - - ciphertext = create_string_buffer(len(plaintext)) - result = _raw_arc4_lib.ARC4_stream_encrypt(self._state.get(), - c_uint8_ptr(plaintext), - ciphertext, - c_size_t(len(plaintext))) - if result: - raise ValueError("Error %d while encrypting with RC4" % result) - return get_raw_buffer(ciphertext) - - def decrypt(self, ciphertext): - """Decrypt a piece of data. - - :param ciphertext: The data to decrypt, of any size. - :type ciphertext: bytes, bytearray, memoryview - :returns: the decrypted byte string, of equal length as the - ciphertext. - """ - - try: - return self.encrypt(ciphertext) - except ValueError as e: - raise ValueError(str(e).replace("enc", "dec")) - - -def new(key, *args, **kwargs): - """Create a new ARC4 cipher. - - :param key: - The secret key to use in the symmetric cipher. - Its length must be in the range ``[1..256]``. - The recommended length is 16 bytes. - :type key: bytes, bytearray, memoryview - - :Keyword Arguments: - * *drop* (``integer``) -- - The amount of bytes to discard from the initial part of the keystream. - In fact, such part has been found to be distinguishable from random - data (while it shouldn't) and also correlated to key. - - The recommended value is 3072_ bytes. The default value is 0. - - :Return: an `ARC4Cipher` object - - .. _3072: http://eprint.iacr.org/2002/067.pdf - """ - return ARC4Cipher(key, *args, **kwargs) - - -# Size of a data block (in bytes) -block_size = 1 -# Size of a key (in bytes) -key_size = range(1, 256+1) diff --git a/resources/lib/deps/Cryptodome/Cipher/ARC4.pyi b/resources/lib/deps/Cryptodome/Cipher/ARC4.pyi deleted file mode 100644 index b081585..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/ARC4.pyi +++ /dev/null @@ -1,16 +0,0 @@ -from typing import Any, Union, Iterable - -Buffer = bytes|bytearray|memoryview - -class ARC4Cipher: - block_size: int - key_size: int - - def __init__(self, key: Buffer, *args: Any, **kwargs: Any) -> None: ... - def encrypt(self, plaintext: Buffer) -> bytes: ... - def decrypt(self, ciphertext: Buffer) -> bytes: ... - -def new(key: Buffer, drop : int = ...) -> ARC4Cipher: ... - -block_size: int -key_size: Iterable[int] diff --git a/resources/lib/deps/Cryptodome/Cipher/Blowfish.py b/resources/lib/deps/Cryptodome/Cipher/Blowfish.py deleted file mode 100644 index 536cbc8..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/Blowfish.py +++ /dev/null @@ -1,159 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Cipher/Blowfish.py : Blowfish -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== -""" -Module's constants for the modes of operation supported with Blowfish: - -:var MODE_ECB: :ref:`Electronic Code Book (ECB) ` -:var MODE_CBC: :ref:`Cipher-Block Chaining (CBC) ` -:var MODE_CFB: :ref:`Cipher FeedBack (CFB) ` -:var MODE_OFB: :ref:`Output FeedBack (OFB) ` -:var MODE_CTR: :ref:`CounTer Mode (CTR) ` -:var MODE_OPENPGP: :ref:`OpenPGP Mode ` -:var MODE_EAX: :ref:`EAX Mode ` -""" - -import sys - -from Cryptodome.Cipher import _create_cipher -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, c_size_t, - c_uint8_ptr) - -_raw_blowfish_lib = load_pycryptodome_raw_lib( - "Cryptodome.Cipher._raw_blowfish", - """ - int Blowfish_start_operation(const uint8_t key[], - size_t key_len, - void **pResult); - int Blowfish_encrypt(const void *state, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int Blowfish_decrypt(const void *state, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int Blowfish_stop_operation(void *state); - """ - ) - - -def _create_base_cipher(dict_parameters): - """This method instantiates and returns a smart pointer to - a low-level base cipher. It will absorb named parameters in - the process.""" - - try: - key = dict_parameters.pop("key") - except KeyError: - raise TypeError("Missing 'key' parameter") - - if len(key) not in key_size: - raise ValueError("Incorrect Blowfish key length (%d bytes)" % len(key)) - - start_operation = _raw_blowfish_lib.Blowfish_start_operation - stop_operation = _raw_blowfish_lib.Blowfish_stop_operation - - void_p = VoidPointer() - result = start_operation(c_uint8_ptr(key), - c_size_t(len(key)), - void_p.address_of()) - if result: - raise ValueError("Error %X while instantiating the Blowfish cipher" - % result) - return SmartPointer(void_p.get(), stop_operation) - - -def new(key, mode, *args, **kwargs): - """Create a new Blowfish cipher - - :param key: - The secret key to use in the symmetric cipher. - Its length can vary from 5 to 56 bytes. - :type key: bytes, bytearray, memoryview - - :param mode: - The chaining mode to use for encryption or decryption. - :type mode: One of the supported ``MODE_*`` constants - - :Keyword Arguments: - * **iv** (*bytes*, *bytearray*, *memoryview*) -- - (Only applicable for ``MODE_CBC``, ``MODE_CFB``, ``MODE_OFB``, - and ``MODE_OPENPGP`` modes). - - The initialization vector to use for encryption or decryption. - - For ``MODE_CBC``, ``MODE_CFB``, and ``MODE_OFB`` it must be 8 bytes long. - - For ``MODE_OPENPGP`` mode only, - it must be 8 bytes long for encryption - and 10 bytes for decryption (in the latter case, it is - actually the *encrypted* IV which was prefixed to the ciphertext). - - If not provided, a random byte string is generated (you must then - read its value with the :attr:`iv` attribute). - - * **nonce** (*bytes*, *bytearray*, *memoryview*) -- - (Only applicable for ``MODE_EAX`` and ``MODE_CTR``). - - A value that must never be reused for any other encryption done - with this key. - - For ``MODE_EAX`` there are no - restrictions on its length (recommended: **16** bytes). - - For ``MODE_CTR``, its length must be in the range **[0..7]**. - - If not provided for ``MODE_EAX``, a random byte string is generated (you - can read it back via the ``nonce`` attribute). - - * **segment_size** (*integer*) -- - (Only ``MODE_CFB``).The number of **bits** the plaintext and ciphertext - are segmented in. It must be a multiple of 8. - If not specified, it will be assumed to be 8. - - * **mac_len** : (*integer*) -- - (Only ``MODE_EAX``) - Length of the authentication tag, in bytes. - It must be no longer than 8 (default). - - * **initial_value** : (*integer*) -- - (Only ``MODE_CTR``). The initial value for the counter within - the counter block. By default it is **0**. - - :Return: a Blowfish object, of the applicable mode. - """ - - return _create_cipher(sys.modules[__name__], key, mode, *args, **kwargs) - -MODE_ECB = 1 -MODE_CBC = 2 -MODE_CFB = 3 -MODE_OFB = 5 -MODE_CTR = 6 -MODE_OPENPGP = 7 -MODE_EAX = 9 - -# Size of a data block (in bytes) -block_size = 8 -# Size of a key (in bytes) -key_size = range(4, 56 + 1) diff --git a/resources/lib/deps/Cryptodome/Cipher/Blowfish.pyi b/resources/lib/deps/Cryptodome/Cipher/Blowfish.pyi deleted file mode 100644 index b8b21c6..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/Blowfish.pyi +++ /dev/null @@ -1,35 +0,0 @@ -from typing import Union, Dict, Iterable, Optional - -Buffer = bytes|bytearray|memoryview - -from Cryptodome.Cipher._mode_ecb import EcbMode -from Cryptodome.Cipher._mode_cbc import CbcMode -from Cryptodome.Cipher._mode_cfb import CfbMode -from Cryptodome.Cipher._mode_ofb import OfbMode -from Cryptodome.Cipher._mode_ctr import CtrMode -from Cryptodome.Cipher._mode_openpgp import OpenPgpMode -from Cryptodome.Cipher._mode_eax import EaxMode - -BlowfishMode = int - -MODE_ECB: BlowfishMode -MODE_CBC: BlowfishMode -MODE_CFB: BlowfishMode -MODE_OFB: BlowfishMode -MODE_CTR: BlowfishMode -MODE_OPENPGP: BlowfishMode -MODE_EAX: BlowfishMode - -def new(key: Buffer, - mode: BlowfishMode, - iv : Optional[Buffer] = ..., - IV : Optional[Buffer] = ..., - nonce : Optional[Buffer] = ..., - segment_size : int = ..., - mac_len : int = ..., - initial_value : Union[int, Buffer] = ..., - counter : Dict = ...) -> \ - Union[EcbMode, CbcMode, CfbMode, OfbMode, CtrMode, OpenPgpMode]: ... - -block_size: int -key_size: Iterable[int] diff --git a/resources/lib/deps/Cryptodome/Cipher/CAST.py b/resources/lib/deps/Cryptodome/Cipher/CAST.py deleted file mode 100644 index 84eb88e..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/CAST.py +++ /dev/null @@ -1,159 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Cipher/CAST.py : CAST -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== -""" -Module's constants for the modes of operation supported with CAST: - -:var MODE_ECB: :ref:`Electronic Code Book (ECB) ` -:var MODE_CBC: :ref:`Cipher-Block Chaining (CBC) ` -:var MODE_CFB: :ref:`Cipher FeedBack (CFB) ` -:var MODE_OFB: :ref:`Output FeedBack (OFB) ` -:var MODE_CTR: :ref:`CounTer Mode (CTR) ` -:var MODE_OPENPGP: :ref:`OpenPGP Mode ` -:var MODE_EAX: :ref:`EAX Mode ` -""" - -import sys - -from Cryptodome.Cipher import _create_cipher -from Cryptodome.Util.py3compat import byte_string -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - c_size_t, c_uint8_ptr) - -_raw_cast_lib = load_pycryptodome_raw_lib( - "Cryptodome.Cipher._raw_cast", - """ - int CAST_start_operation(const uint8_t key[], - size_t key_len, - void **pResult); - int CAST_encrypt(const void *state, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int CAST_decrypt(const void *state, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int CAST_stop_operation(void *state); - """) - - -def _create_base_cipher(dict_parameters): - """This method instantiates and returns a handle to a low-level - base cipher. It will absorb named parameters in the process.""" - - try: - key = dict_parameters.pop("key") - except KeyError: - raise TypeError("Missing 'key' parameter") - - if len(key) not in key_size: - raise ValueError("Incorrect CAST key length (%d bytes)" % len(key)) - - start_operation = _raw_cast_lib.CAST_start_operation - stop_operation = _raw_cast_lib.CAST_stop_operation - - cipher = VoidPointer() - result = start_operation(c_uint8_ptr(key), - c_size_t(len(key)), - cipher.address_of()) - if result: - raise ValueError("Error %X while instantiating the CAST cipher" - % result) - - return SmartPointer(cipher.get(), stop_operation) - - -def new(key, mode, *args, **kwargs): - """Create a new CAST cipher - - :param key: - The secret key to use in the symmetric cipher. - Its length can vary from 5 to 16 bytes. - :type key: bytes, bytearray, memoryview - - :param mode: - The chaining mode to use for encryption or decryption. - :type mode: One of the supported ``MODE_*`` constants - - :Keyword Arguments: - * **iv** (*bytes*, *bytearray*, *memoryview*) -- - (Only applicable for ``MODE_CBC``, ``MODE_CFB``, ``MODE_OFB``, - and ``MODE_OPENPGP`` modes). - - The initialization vector to use for encryption or decryption. - - For ``MODE_CBC``, ``MODE_CFB``, and ``MODE_OFB`` it must be 8 bytes long. - - For ``MODE_OPENPGP`` mode only, - it must be 8 bytes long for encryption - and 10 bytes for decryption (in the latter case, it is - actually the *encrypted* IV which was prefixed to the ciphertext). - - If not provided, a random byte string is generated (you must then - read its value with the :attr:`iv` attribute). - - * **nonce** (*bytes*, *bytearray*, *memoryview*) -- - (Only applicable for ``MODE_EAX`` and ``MODE_CTR``). - - A value that must never be reused for any other encryption done - with this key. - - For ``MODE_EAX`` there are no - restrictions on its length (recommended: **16** bytes). - - For ``MODE_CTR``, its length must be in the range **[0..7]**. - - If not provided for ``MODE_EAX``, a random byte string is generated (you - can read it back via the ``nonce`` attribute). - - * **segment_size** (*integer*) -- - (Only ``MODE_CFB``).The number of **bits** the plaintext and ciphertext - are segmented in. It must be a multiple of 8. - If not specified, it will be assumed to be 8. - - * **mac_len** : (*integer*) -- - (Only ``MODE_EAX``) - Length of the authentication tag, in bytes. - It must be no longer than 8 (default). - - * **initial_value** : (*integer*) -- - (Only ``MODE_CTR``). The initial value for the counter within - the counter block. By default it is **0**. - - :Return: a CAST object, of the applicable mode. - """ - - return _create_cipher(sys.modules[__name__], key, mode, *args, **kwargs) - -MODE_ECB = 1 -MODE_CBC = 2 -MODE_CFB = 3 -MODE_OFB = 5 -MODE_CTR = 6 -MODE_OPENPGP = 7 -MODE_EAX = 9 - -# Size of a data block (in bytes) -block_size = 8 -# Size of a key (in bytes) -key_size = range(5, 16 + 1) diff --git a/resources/lib/deps/Cryptodome/Cipher/CAST.pyi b/resources/lib/deps/Cryptodome/Cipher/CAST.pyi deleted file mode 100644 index be01f09..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/CAST.pyi +++ /dev/null @@ -1,35 +0,0 @@ -from typing import Union, Dict, Iterable, Optional - -Buffer = bytes|bytearray|memoryview - -from Cryptodome.Cipher._mode_ecb import EcbMode -from Cryptodome.Cipher._mode_cbc import CbcMode -from Cryptodome.Cipher._mode_cfb import CfbMode -from Cryptodome.Cipher._mode_ofb import OfbMode -from Cryptodome.Cipher._mode_ctr import CtrMode -from Cryptodome.Cipher._mode_openpgp import OpenPgpMode -from Cryptodome.Cipher._mode_eax import EaxMode - -CASTMode = int - -MODE_ECB: CASTMode -MODE_CBC: CASTMode -MODE_CFB: CASTMode -MODE_OFB: CASTMode -MODE_CTR: CASTMode -MODE_OPENPGP: CASTMode -MODE_EAX: CASTMode - -def new(key: Buffer, - mode: CASTMode, - iv : Optional[Buffer] = ..., - IV : Optional[Buffer] = ..., - nonce : Optional[Buffer] = ..., - segment_size : int = ..., - mac_len : int = ..., - initial_value : Union[int, Buffer] = ..., - counter : Dict = ...) -> \ - Union[EcbMode, CbcMode, CfbMode, OfbMode, CtrMode, OpenPgpMode]: ... - -block_size: int -key_size : Iterable[int] diff --git a/resources/lib/deps/Cryptodome/Cipher/ChaCha20.py b/resources/lib/deps/Cryptodome/Cipher/ChaCha20.py deleted file mode 100644 index 648d692..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/ChaCha20.py +++ /dev/null @@ -1,287 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from Cryptodome.Random import get_random_bytes - -from Cryptodome.Util.py3compat import _copy_bytes -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - create_string_buffer, - get_raw_buffer, VoidPointer, - SmartPointer, c_size_t, - c_uint8_ptr, c_ulong, - is_writeable_buffer) - -_raw_chacha20_lib = load_pycryptodome_raw_lib("Cryptodome.Cipher._chacha20", - """ - int chacha20_init(void **pState, - const uint8_t *key, - size_t keySize, - const uint8_t *nonce, - size_t nonceSize); - - int chacha20_destroy(void *state); - - int chacha20_encrypt(void *state, - const uint8_t in[], - uint8_t out[], - size_t len); - - int chacha20_seek(void *state, - unsigned long block_high, - unsigned long block_low, - unsigned offset); - int hchacha20( const uint8_t key[32], - const uint8_t nonce16[16], - uint8_t subkey[32]); - """) - - -def _HChaCha20(key, nonce): - - assert(len(key) == 32) - assert(len(nonce) == 16) - - subkey = bytearray(32) - result = _raw_chacha20_lib.hchacha20( - c_uint8_ptr(key), - c_uint8_ptr(nonce), - c_uint8_ptr(subkey)) - if result: - raise ValueError("Error %d when deriving subkey with HChaCha20" % result) - - return subkey - - -class ChaCha20Cipher(object): - """ChaCha20 (or XChaCha20) cipher object. - Do not create it directly. Use :py:func:`new` instead. - - :var nonce: The nonce with length 8, 12 or 24 bytes - :vartype nonce: bytes - """ - - block_size = 1 - - def __init__(self, key, nonce): - """Initialize a ChaCha20/XChaCha20 cipher object - - See also `new()` at the module level.""" - - self.nonce = _copy_bytes(None, None, nonce) - - # XChaCha20 requires a key derivation with HChaCha20 - # See 2.3 in https://tools.ietf.org/html/draft-arciszewski-xchacha-03 - if len(nonce) == 24: - key = _HChaCha20(key, nonce[:16]) - nonce = b'\x00' * 4 + nonce[16:] - self._name = "XChaCha20" - else: - self._name = "ChaCha20" - nonce = self.nonce - - self._next = ("encrypt", "decrypt") - - self._state = VoidPointer() - result = _raw_chacha20_lib.chacha20_init( - self._state.address_of(), - c_uint8_ptr(key), - c_size_t(len(key)), - nonce, - c_size_t(len(nonce))) - if result: - raise ValueError("Error %d instantiating a %s cipher" % (result, - self._name)) - self._state = SmartPointer(self._state.get(), - _raw_chacha20_lib.chacha20_destroy) - - def encrypt(self, plaintext, output=None): - """Encrypt a piece of data. - - Args: - plaintext(bytes/bytearray/memoryview): The data to encrypt, of any size. - Keyword Args: - output(bytes/bytearray/memoryview): The location where the ciphertext - is written to. If ``None``, the ciphertext is returned. - Returns: - If ``output`` is ``None``, the ciphertext is returned as ``bytes``. - Otherwise, ``None``. - """ - - if "encrypt" not in self._next: - raise TypeError("Cipher object can only be used for decryption") - self._next = ("encrypt",) - return self._encrypt(plaintext, output) - - def _encrypt(self, plaintext, output): - """Encrypt without FSM checks""" - - if output is None: - ciphertext = create_string_buffer(len(plaintext)) - else: - ciphertext = output - - if not is_writeable_buffer(output): - raise TypeError("output must be a bytearray or a writeable memoryview") - - if len(plaintext) != len(output): - raise ValueError("output must have the same length as the input" - " (%d bytes)" % len(plaintext)) - - result = _raw_chacha20_lib.chacha20_encrypt( - self._state.get(), - c_uint8_ptr(plaintext), - c_uint8_ptr(ciphertext), - c_size_t(len(plaintext))) - if result: - raise ValueError("Error %d while encrypting with %s" % (result, self._name)) - - if output is None: - return get_raw_buffer(ciphertext) - else: - return None - - def decrypt(self, ciphertext, output=None): - """Decrypt a piece of data. - - Args: - ciphertext(bytes/bytearray/memoryview): The data to decrypt, of any size. - Keyword Args: - output(bytes/bytearray/memoryview): The location where the plaintext - is written to. If ``None``, the plaintext is returned. - Returns: - If ``output`` is ``None``, the plaintext is returned as ``bytes``. - Otherwise, ``None``. - """ - - if "decrypt" not in self._next: - raise TypeError("Cipher object can only be used for encryption") - self._next = ("decrypt",) - - try: - return self._encrypt(ciphertext, output) - except ValueError as e: - raise ValueError(str(e).replace("enc", "dec")) - - def seek(self, position): - """Seek to a certain position in the key stream. - - Args: - position (integer): - The absolute position within the key stream, in bytes. - """ - - position, offset = divmod(position, 64) - block_low = position & 0xFFFFFFFF - block_high = position >> 32 - - result = _raw_chacha20_lib.chacha20_seek( - self._state.get(), - c_ulong(block_high), - c_ulong(block_low), - offset - ) - if result: - raise ValueError("Error %d while seeking with %s" % (result, self._name)) - - -def _derive_Poly1305_key_pair(key, nonce): - """Derive a tuple (r, s, nonce) for a Poly1305 MAC. - - If nonce is ``None``, a new 12-byte nonce is generated. - """ - - if len(key) != 32: - raise ValueError("Poly1305 with ChaCha20 requires a 32-byte key") - - if nonce is None: - padded_nonce = nonce = get_random_bytes(12) - elif len(nonce) == 8: - # See RFC7538, 2.6: [...] ChaCha20 as specified here requires a 96-bit - # nonce. So if the provided nonce is only 64-bit, then the first 32 - # bits of the nonce will be set to a constant number. - # This will usually be zero, but for protocols with multiple senders it may be - # different for each sender, but should be the same for all - # invocations of the function with the same key by a particular - # sender. - padded_nonce = b'\x00\x00\x00\x00' + nonce - elif len(nonce) == 12: - padded_nonce = nonce - else: - raise ValueError("Poly1305 with ChaCha20 requires an 8- or 12-byte nonce") - - rs = new(key=key, nonce=padded_nonce).encrypt(b'\x00' * 32) - return rs[:16], rs[16:], nonce - - -def new(**kwargs): - """Create a new ChaCha20 or XChaCha20 cipher - - Keyword Args: - key (bytes/bytearray/memoryview): The secret key to use. - It must be 32 bytes long. - nonce (bytes/bytearray/memoryview): A mandatory value that - must never be reused for any other encryption - done with this key. - - For ChaCha20, it must be 8 or 12 bytes long. - - For XChaCha20, it must be 24 bytes long. - - If not provided, 8 bytes will be randomly generated - (you can find them back in the ``nonce`` attribute). - - :Return: a :class:`Cryptodome.Cipher.ChaCha20.ChaCha20Cipher` object - """ - - try: - key = kwargs.pop("key") - except KeyError as e: - raise TypeError("Missing parameter %s" % e) - - nonce = kwargs.pop("nonce", None) - if nonce is None: - nonce = get_random_bytes(8) - - if len(key) != 32: - raise ValueError("ChaCha20/XChaCha20 key must be 32 bytes long") - - if len(nonce) not in (8, 12, 24): - raise ValueError("Nonce must be 8/12 bytes(ChaCha20) or 24 bytes (XChaCha20)") - - if kwargs: - raise TypeError("Unknown parameters: " + str(kwargs)) - - return ChaCha20Cipher(key, nonce) - -# Size of a data block (in bytes) -block_size = 1 - -# Size of a key (in bytes) -key_size = 32 diff --git a/resources/lib/deps/Cryptodome/Cipher/ChaCha20.pyi b/resources/lib/deps/Cryptodome/Cipher/ChaCha20.pyi deleted file mode 100644 index f5001cd..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/ChaCha20.pyi +++ /dev/null @@ -1,25 +0,0 @@ -from typing import Union, overload, Optional - -Buffer = bytes|bytearray|memoryview - -def _HChaCha20(key: Buffer, nonce: Buffer) -> bytearray: ... - -class ChaCha20Cipher: - block_size: int - nonce: bytes - - def __init__(self, key: Buffer, nonce: Buffer) -> None: ... - @overload - def encrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def encrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - @overload - def decrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def decrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - def seek(self, position: int) -> None: ... - -def new(key: Buffer, nonce: Optional[Buffer] = ...) -> ChaCha20Cipher: ... - -block_size: int -key_size: int diff --git a/resources/lib/deps/Cryptodome/Cipher/ChaCha20_Poly1305.py b/resources/lib/deps/Cryptodome/Cipher/ChaCha20_Poly1305.py deleted file mode 100644 index b2923ed..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/ChaCha20_Poly1305.py +++ /dev/null @@ -1,336 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2018, Helder Eijs -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from binascii import unhexlify - -from Cryptodome.Cipher import ChaCha20 -from Cryptodome.Cipher.ChaCha20 import _HChaCha20 -from Cryptodome.Hash import Poly1305, BLAKE2s - -from Cryptodome.Random import get_random_bytes - -from Cryptodome.Util.number import long_to_bytes -from Cryptodome.Util.py3compat import _copy_bytes, bord -from Cryptodome.Util._raw_api import is_buffer - - -def _enum(**enums): - return type('Enum', (), enums) - - -_CipherStatus = _enum(PROCESSING_AUTH_DATA=1, - PROCESSING_CIPHERTEXT=2, - PROCESSING_DONE=3) - - -class ChaCha20Poly1305Cipher(object): - """ChaCha20-Poly1305 and XChaCha20-Poly1305 cipher object. - Do not create it directly. Use :py:func:`new` instead. - - :var nonce: The nonce with length 8, 12 or 24 bytes - :vartype nonce: byte string - """ - - def __init__(self, key, nonce): - """Initialize a ChaCha20-Poly1305 AEAD cipher object - - See also `new()` at the module level.""" - - self._next = ("update", "encrypt", "decrypt", "digest", - "verify") - - self._authenticator = Poly1305.new(key=key, nonce=nonce, cipher=ChaCha20) - - self._cipher = ChaCha20.new(key=key, nonce=nonce) - self._cipher.seek(64) # Block counter starts at 1 - - self._len_aad = 0 - self._len_ct = 0 - self._mac_tag = None - self._status = _CipherStatus.PROCESSING_AUTH_DATA - - def update(self, data): - """Protect the associated data. - - Associated data (also known as *additional authenticated data* - AAD) - is the piece of the message that must stay in the clear, while - still allowing the receiver to verify its integrity. - An example is packet headers. - - The associated data (possibly split into multiple segments) is - fed into :meth:`update` before any call to :meth:`decrypt` or :meth:`encrypt`. - If there is no associated data, :meth:`update` is not called. - - :param bytes/bytearray/memoryview assoc_data: - A piece of associated data. There are no restrictions on its size. - """ - - if "update" not in self._next: - raise TypeError("update() method cannot be called") - - self._len_aad += len(data) - self._authenticator.update(data) - - def _pad_aad(self): - - assert(self._status == _CipherStatus.PROCESSING_AUTH_DATA) - if self._len_aad & 0x0F: - self._authenticator.update(b'\x00' * (16 - (self._len_aad & 0x0F))) - self._status = _CipherStatus.PROCESSING_CIPHERTEXT - - def encrypt(self, plaintext, output=None): - """Encrypt a piece of data. - - Args: - plaintext(bytes/bytearray/memoryview): The data to encrypt, of any size. - Keyword Args: - output(bytes/bytearray/memoryview): The location where the ciphertext - is written to. If ``None``, the ciphertext is returned. - Returns: - If ``output`` is ``None``, the ciphertext is returned as ``bytes``. - Otherwise, ``None``. - """ - - if "encrypt" not in self._next: - raise TypeError("encrypt() method cannot be called") - - if self._status == _CipherStatus.PROCESSING_AUTH_DATA: - self._pad_aad() - - self._next = ("encrypt", "digest") - - result = self._cipher.encrypt(plaintext, output=output) - self._len_ct += len(plaintext) - if output is None: - self._authenticator.update(result) - else: - self._authenticator.update(output) - return result - - def decrypt(self, ciphertext, output=None): - """Decrypt a piece of data. - - Args: - ciphertext(bytes/bytearray/memoryview): The data to decrypt, of any size. - Keyword Args: - output(bytes/bytearray/memoryview): The location where the plaintext - is written to. If ``None``, the plaintext is returned. - Returns: - If ``output`` is ``None``, the plaintext is returned as ``bytes``. - Otherwise, ``None``. - """ - - if "decrypt" not in self._next: - raise TypeError("decrypt() method cannot be called") - - if self._status == _CipherStatus.PROCESSING_AUTH_DATA: - self._pad_aad() - - self._next = ("decrypt", "verify") - - self._len_ct += len(ciphertext) - self._authenticator.update(ciphertext) - return self._cipher.decrypt(ciphertext, output=output) - - def _compute_mac(self): - """Finalize the cipher (if not done already) and return the MAC.""" - - if self._mac_tag: - assert(self._status == _CipherStatus.PROCESSING_DONE) - return self._mac_tag - - assert(self._status != _CipherStatus.PROCESSING_DONE) - - if self._status == _CipherStatus.PROCESSING_AUTH_DATA: - self._pad_aad() - - if self._len_ct & 0x0F: - self._authenticator.update(b'\x00' * (16 - (self._len_ct & 0x0F))) - - self._status = _CipherStatus.PROCESSING_DONE - - self._authenticator.update(long_to_bytes(self._len_aad, 8)[::-1]) - self._authenticator.update(long_to_bytes(self._len_ct, 8)[::-1]) - self._mac_tag = self._authenticator.digest() - return self._mac_tag - - def digest(self): - """Compute the *binary* authentication tag (MAC). - - :Return: the MAC tag, as 16 ``bytes``. - """ - - if "digest" not in self._next: - raise TypeError("digest() method cannot be called") - self._next = ("digest",) - - return self._compute_mac() - - def hexdigest(self): - """Compute the *printable* authentication tag (MAC). - - This method is like :meth:`digest`. - - :Return: the MAC tag, as a hexadecimal string. - """ - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def verify(self, received_mac_tag): - """Validate the *binary* authentication tag (MAC). - - The receiver invokes this method at the very end, to - check if the associated data (if any) and the decrypted - messages are valid. - - :param bytes/bytearray/memoryview received_mac_tag: - This is the 16-byte *binary* MAC, as received from the sender. - :Raises ValueError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - if "verify" not in self._next: - raise TypeError("verify() cannot be called" - " when encrypting a message") - self._next = ("verify",) - - secret = get_random_bytes(16) - - self._compute_mac() - - mac1 = BLAKE2s.new(digest_bits=160, key=secret, - data=self._mac_tag) - mac2 = BLAKE2s.new(digest_bits=160, key=secret, - data=received_mac_tag) - - if mac1.digest() != mac2.digest(): - raise ValueError("MAC check failed") - - def hexverify(self, hex_mac_tag): - """Validate the *printable* authentication tag (MAC). - - This method is like :meth:`verify`. - - :param string hex_mac_tag: - This is the *printable* MAC. - :Raises ValueError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - self.verify(unhexlify(hex_mac_tag)) - - def encrypt_and_digest(self, plaintext): - """Perform :meth:`encrypt` and :meth:`digest` in one step. - - :param plaintext: The data to encrypt, of any size. - :type plaintext: bytes/bytearray/memoryview - :return: a tuple with two ``bytes`` objects: - - - the ciphertext, of equal length as the plaintext - - the 16-byte MAC tag - """ - - return self.encrypt(plaintext), self.digest() - - def decrypt_and_verify(self, ciphertext, received_mac_tag): - """Perform :meth:`decrypt` and :meth:`verify` in one step. - - :param ciphertext: The piece of data to decrypt. - :type ciphertext: bytes/bytearray/memoryview - :param bytes received_mac_tag: - This is the 16-byte *binary* MAC, as received from the sender. - :return: the decrypted data (as ``bytes``) - :raises ValueError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - plaintext = self.decrypt(ciphertext) - self.verify(received_mac_tag) - return plaintext - - -def new(**kwargs): - """Create a new ChaCha20-Poly1305 or XChaCha20-Poly1305 AEAD cipher. - - :keyword key: The secret key to use. It must be 32 bytes long. - :type key: byte string - - :keyword nonce: - A value that must never be reused for any other encryption - done with this key. - - For ChaCha20-Poly1305, it must be 8 or 12 bytes long. - - For XChaCha20-Poly1305, it must be 24 bytes long. - - If not provided, 12 ``bytes`` will be generated randomly - (you can find them back in the ``nonce`` attribute). - :type nonce: bytes, bytearray, memoryview - - :Return: a :class:`Cryptodome.Cipher.ChaCha20.ChaCha20Poly1305Cipher` object - """ - - try: - key = kwargs.pop("key") - except KeyError as e: - raise TypeError("Missing parameter %s" % e) - - self._len_ct += len(plaintext) - - if len(key) != 32: - raise ValueError("Key must be 32 bytes long") - - nonce = kwargs.pop("nonce", None) - if nonce is None: - nonce = get_random_bytes(12) - - if len(nonce) in (8, 12): - chacha20_poly1305_nonce = nonce - elif len(nonce) == 24: - key = _HChaCha20(key, nonce[:16]) - chacha20_poly1305_nonce = b'\x00\x00\x00\x00' + nonce[16:] - else: - raise ValueError("Nonce must be 8, 12 or 24 bytes long") - - if not is_buffer(nonce): - raise TypeError("nonce must be bytes, bytearray or memoryview") - - if kwargs: - raise TypeError("Unknown parameters: " + str(kwargs)) - - cipher = ChaCha20Poly1305Cipher(key, chacha20_poly1305_nonce) - cipher.nonce = _copy_bytes(None, None, nonce) - return cipher - - -# Size of a key (in bytes) -key_size = 32 diff --git a/resources/lib/deps/Cryptodome/Cipher/ChaCha20_Poly1305.pyi b/resources/lib/deps/Cryptodome/Cipher/ChaCha20_Poly1305.pyi deleted file mode 100644 index 109e805..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/ChaCha20_Poly1305.pyi +++ /dev/null @@ -1,28 +0,0 @@ -from typing import Union, Tuple, overload, Optional - -Buffer = bytes|bytearray|memoryview - -class ChaCha20Poly1305Cipher: - nonce: bytes - - def __init__(self, key: Buffer, nonce: Buffer) -> None: ... - def update(self, data: Buffer) -> None: ... - @overload - def encrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def encrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - @overload - def decrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def decrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def verify(self, received_mac_tag: Buffer) -> None: ... - def hexverify(self, received_mac_tag: str) -> None: ... - def encrypt_and_digest(self, plaintext: Buffer) -> Tuple[bytes, bytes]: ... - def decrypt_and_verify(self, ciphertext: Buffer, received_mac_tag: Buffer) -> bytes: ... - -def new(key: Buffer, nonce: Optional[Buffer] = ...) -> ChaCha20Poly1305Cipher: ... - -block_size: int -key_size: int diff --git a/resources/lib/deps/Cryptodome/Cipher/DES.py b/resources/lib/deps/Cryptodome/Cipher/DES.py deleted file mode 100644 index 026b491..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/DES.py +++ /dev/null @@ -1,158 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Cipher/DES.py : DES -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== -""" -Module's constants for the modes of operation supported with Single DES: - -:var MODE_ECB: :ref:`Electronic Code Book (ECB) ` -:var MODE_CBC: :ref:`Cipher-Block Chaining (CBC) ` -:var MODE_CFB: :ref:`Cipher FeedBack (CFB) ` -:var MODE_OFB: :ref:`Output FeedBack (OFB) ` -:var MODE_CTR: :ref:`CounTer Mode (CTR) ` -:var MODE_OPENPGP: :ref:`OpenPGP Mode ` -:var MODE_EAX: :ref:`EAX Mode ` -""" - -import sys - -from Cryptodome.Cipher import _create_cipher -from Cryptodome.Util.py3compat import byte_string -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - c_size_t, c_uint8_ptr) - -_raw_des_lib = load_pycryptodome_raw_lib( - "Cryptodome.Cipher._raw_des", - """ - int DES_start_operation(const uint8_t key[], - size_t key_len, - void **pResult); - int DES_encrypt(const void *state, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int DES_decrypt(const void *state, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int DES_stop_operation(void *state); - """) - - -def _create_base_cipher(dict_parameters): - """This method instantiates and returns a handle to a low-level - base cipher. It will absorb named parameters in the process.""" - - try: - key = dict_parameters.pop("key") - except KeyError: - raise TypeError("Missing 'key' parameter") - - if len(key) != key_size: - raise ValueError("Incorrect DES key length (%d bytes)" % len(key)) - - start_operation = _raw_des_lib.DES_start_operation - stop_operation = _raw_des_lib.DES_stop_operation - - cipher = VoidPointer() - result = start_operation(c_uint8_ptr(key), - c_size_t(len(key)), - cipher.address_of()) - if result: - raise ValueError("Error %X while instantiating the DES cipher" - % result) - return SmartPointer(cipher.get(), stop_operation) - - -def new(key, mode, *args, **kwargs): - """Create a new DES cipher. - - :param key: - The secret key to use in the symmetric cipher. - It must be 8 byte long. The parity bits will be ignored. - :type key: bytes/bytearray/memoryview - - :param mode: - The chaining mode to use for encryption or decryption. - :type mode: One of the supported ``MODE_*`` constants - - :Keyword Arguments: - * **iv** (*byte string*) -- - (Only applicable for ``MODE_CBC``, ``MODE_CFB``, ``MODE_OFB``, - and ``MODE_OPENPGP`` modes). - - The initialization vector to use for encryption or decryption. - - For ``MODE_CBC``, ``MODE_CFB``, and ``MODE_OFB`` it must be 8 bytes long. - - For ``MODE_OPENPGP`` mode only, - it must be 8 bytes long for encryption - and 10 bytes for decryption (in the latter case, it is - actually the *encrypted* IV which was prefixed to the ciphertext). - - If not provided, a random byte string is generated (you must then - read its value with the :attr:`iv` attribute). - - * **nonce** (*byte string*) -- - (Only applicable for ``MODE_EAX`` and ``MODE_CTR``). - - A value that must never be reused for any other encryption done - with this key. - - For ``MODE_EAX`` there are no - restrictions on its length (recommended: **16** bytes). - - For ``MODE_CTR``, its length must be in the range **[0..7]**. - - If not provided for ``MODE_EAX``, a random byte string is generated (you - can read it back via the ``nonce`` attribute). - - * **segment_size** (*integer*) -- - (Only ``MODE_CFB``).The number of **bits** the plaintext and ciphertext - are segmented in. It must be a multiple of 8. - If not specified, it will be assumed to be 8. - - * **mac_len** : (*integer*) -- - (Only ``MODE_EAX``) - Length of the authentication tag, in bytes. - It must be no longer than 8 (default). - - * **initial_value** : (*integer*) -- - (Only ``MODE_CTR``). The initial value for the counter within - the counter block. By default it is **0**. - - :Return: a DES object, of the applicable mode. - """ - - return _create_cipher(sys.modules[__name__], key, mode, *args, **kwargs) - -MODE_ECB = 1 -MODE_CBC = 2 -MODE_CFB = 3 -MODE_OFB = 5 -MODE_CTR = 6 -MODE_OPENPGP = 7 -MODE_EAX = 9 - -# Size of a data block (in bytes) -block_size = 8 -# Size of a key (in bytes) -key_size = 8 diff --git a/resources/lib/deps/Cryptodome/Cipher/DES.pyi b/resources/lib/deps/Cryptodome/Cipher/DES.pyi deleted file mode 100644 index 25a3b23..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/DES.pyi +++ /dev/null @@ -1,35 +0,0 @@ -from typing import Union, Dict, Iterable, Optional - -Buffer = bytes|bytearray|memoryview - -from Cryptodome.Cipher._mode_ecb import EcbMode -from Cryptodome.Cipher._mode_cbc import CbcMode -from Cryptodome.Cipher._mode_cfb import CfbMode -from Cryptodome.Cipher._mode_ofb import OfbMode -from Cryptodome.Cipher._mode_ctr import CtrMode -from Cryptodome.Cipher._mode_openpgp import OpenPgpMode -from Cryptodome.Cipher._mode_eax import EaxMode - -DESMode = int - -MODE_ECB: DESMode -MODE_CBC: DESMode -MODE_CFB: DESMode -MODE_OFB: DESMode -MODE_CTR: DESMode -MODE_OPENPGP: DESMode -MODE_EAX: DESMode - -def new(key: Buffer, - mode: DESMode, - iv : Optional[Buffer] = ..., - IV : Optional[Buffer] = ..., - nonce : Optional[Buffer] = ..., - segment_size : int = ..., - mac_len : int = ..., - initial_value : Union[int, Buffer] = ..., - counter : Dict = ...) -> \ - Union[EcbMode, CbcMode, CfbMode, OfbMode, CtrMode, OpenPgpMode]: ... - -block_size: int -key_size: int diff --git a/resources/lib/deps/Cryptodome/Cipher/DES3.py b/resources/lib/deps/Cryptodome/Cipher/DES3.py deleted file mode 100644 index 3b2828e..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/DES3.py +++ /dev/null @@ -1,187 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Cipher/DES3.py : DES3 -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== -""" -Module's constants for the modes of operation supported with Triple DES: - -:var MODE_ECB: :ref:`Electronic Code Book (ECB) ` -:var MODE_CBC: :ref:`Cipher-Block Chaining (CBC) ` -:var MODE_CFB: :ref:`Cipher FeedBack (CFB) ` -:var MODE_OFB: :ref:`Output FeedBack (OFB) ` -:var MODE_CTR: :ref:`CounTer Mode (CTR) ` -:var MODE_OPENPGP: :ref:`OpenPGP Mode ` -:var MODE_EAX: :ref:`EAX Mode ` -""" - -import sys - -from Cryptodome.Cipher import _create_cipher -from Cryptodome.Util.py3compat import byte_string, bchr, bord, bstr -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - c_size_t) - -_raw_des3_lib = load_pycryptodome_raw_lib( - "Cryptodome.Cipher._raw_des3", - """ - int DES3_start_operation(const uint8_t key[], - size_t key_len, - void **pResult); - int DES3_encrypt(const void *state, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int DES3_decrypt(const void *state, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int DES3_stop_operation(void *state); - """) - - -def adjust_key_parity(key_in): - """Set the parity bits in a TDES key. - - :param key_in: the TDES key whose bits need to be adjusted - :type key_in: byte string - - :returns: a copy of ``key_in``, with the parity bits correctly set - :rtype: byte string - - :raises ValueError: if the TDES key is not 16 or 24 bytes long - :raises ValueError: if the TDES key degenerates into Single DES - """ - - def parity_byte(key_byte): - parity = 1 - for i in range(1, 8): - parity ^= (key_byte >> i) & 1 - return (key_byte & 0xFE) | parity - - if len(key_in) not in key_size: - raise ValueError("Not a valid TDES key") - - key_out = b"".join([ bchr(parity_byte(bord(x))) for x in key_in ]) - - if key_out[:8] == key_out[8:16] or key_out[-16:-8] == key_out[-8:]: - raise ValueError("Triple DES key degenerates to single DES") - - return key_out - - -def _create_base_cipher(dict_parameters): - """This method instantiates and returns a handle to a low-level base cipher. - It will absorb named parameters in the process.""" - - try: - key_in = dict_parameters.pop("key") - except KeyError: - raise TypeError("Missing 'key' parameter") - - key = adjust_key_parity(bstr(key_in)) - - start_operation = _raw_des3_lib.DES3_start_operation - stop_operation = _raw_des3_lib.DES3_stop_operation - - cipher = VoidPointer() - result = start_operation(key, - c_size_t(len(key)), - cipher.address_of()) - if result: - raise ValueError("Error %X while instantiating the TDES cipher" - % result) - return SmartPointer(cipher.get(), stop_operation) - - -def new(key, mode, *args, **kwargs): - """Create a new Triple DES cipher. - - :param key: - The secret key to use in the symmetric cipher. - It must be 16 or 24 byte long. The parity bits will be ignored. - :type key: bytes/bytearray/memoryview - - :param mode: - The chaining mode to use for encryption or decryption. - :type mode: One of the supported ``MODE_*`` constants - - :Keyword Arguments: - * **iv** (*bytes*, *bytearray*, *memoryview*) -- - (Only applicable for ``MODE_CBC``, ``MODE_CFB``, ``MODE_OFB``, - and ``MODE_OPENPGP`` modes). - - The initialization vector to use for encryption or decryption. - - For ``MODE_CBC``, ``MODE_CFB``, and ``MODE_OFB`` it must be 8 bytes long. - - For ``MODE_OPENPGP`` mode only, - it must be 8 bytes long for encryption - and 10 bytes for decryption (in the latter case, it is - actually the *encrypted* IV which was prefixed to the ciphertext). - - If not provided, a random byte string is generated (you must then - read its value with the :attr:`iv` attribute). - - * **nonce** (*bytes*, *bytearray*, *memoryview*) -- - (Only applicable for ``MODE_EAX`` and ``MODE_CTR``). - - A value that must never be reused for any other encryption done - with this key. - - For ``MODE_EAX`` there are no - restrictions on its length (recommended: **16** bytes). - - For ``MODE_CTR``, its length must be in the range **[0..7]**. - - If not provided for ``MODE_EAX``, a random byte string is generated (you - can read it back via the ``nonce`` attribute). - - * **segment_size** (*integer*) -- - (Only ``MODE_CFB``).The number of **bits** the plaintext and ciphertext - are segmented in. It must be a multiple of 8. - If not specified, it will be assumed to be 8. - - * **mac_len** : (*integer*) -- - (Only ``MODE_EAX``) - Length of the authentication tag, in bytes. - It must be no longer than 8 (default). - - * **initial_value** : (*integer*) -- - (Only ``MODE_CTR``). The initial value for the counter within - the counter block. By default it is **0**. - - :Return: a Triple DES object, of the applicable mode. - """ - - return _create_cipher(sys.modules[__name__], key, mode, *args, **kwargs) - -MODE_ECB = 1 -MODE_CBC = 2 -MODE_CFB = 3 -MODE_OFB = 5 -MODE_CTR = 6 -MODE_OPENPGP = 7 -MODE_EAX = 9 - -# Size of a data block (in bytes) -block_size = 8 -# Size of a key (in bytes) -key_size = (16, 24) diff --git a/resources/lib/deps/Cryptodome/Cipher/DES3.pyi b/resources/lib/deps/Cryptodome/Cipher/DES3.pyi deleted file mode 100644 index 2c150f8..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/DES3.pyi +++ /dev/null @@ -1,37 +0,0 @@ -from typing import Union, Dict, Tuple, Optional - -Buffer = bytes|bytearray|memoryview - -from Cryptodome.Cipher._mode_ecb import EcbMode -from Cryptodome.Cipher._mode_cbc import CbcMode -from Cryptodome.Cipher._mode_cfb import CfbMode -from Cryptodome.Cipher._mode_ofb import OfbMode -from Cryptodome.Cipher._mode_ctr import CtrMode -from Cryptodome.Cipher._mode_openpgp import OpenPgpMode -from Cryptodome.Cipher._mode_eax import EaxMode - -def adjust_key_parity(key_in: bytes) -> bytes: ... - -DES3Mode = int - -MODE_ECB: DES3Mode -MODE_CBC: DES3Mode -MODE_CFB: DES3Mode -MODE_OFB: DES3Mode -MODE_CTR: DES3Mode -MODE_OPENPGP: DES3Mode -MODE_EAX: DES3Mode - -def new(key: Buffer, - mode: DES3Mode, - iv : Optional[Buffer] = ..., - IV : Optional[Buffer] = ..., - nonce : Optional[Buffer] = ..., - segment_size : int = ..., - mac_len : int = ..., - initial_value : Union[int, Buffer] = ..., - counter : Dict = ...) -> \ - Union[EcbMode, CbcMode, CfbMode, OfbMode, CtrMode, OpenPgpMode]: ... - -block_size: int -key_size: Tuple[int, int] diff --git a/resources/lib/deps/Cryptodome/Cipher/PKCS1_OAEP.py b/resources/lib/deps/Cryptodome/Cipher/PKCS1_OAEP.py deleted file mode 100644 index 08f9efe..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/PKCS1_OAEP.py +++ /dev/null @@ -1,231 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Cipher/PKCS1_OAEP.py : PKCS#1 OAEP -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -from Cryptodome.Signature.pss import MGF1 -import Cryptodome.Hash.SHA1 - -from Cryptodome.Util.py3compat import _copy_bytes -import Cryptodome.Util.number -from Cryptodome.Util.number import ceil_div, bytes_to_long, long_to_bytes -from Cryptodome.Util.strxor import strxor -from Cryptodome import Random -from ._pkcs1_oaep_decode import oaep_decode - - -class PKCS1OAEP_Cipher: - """Cipher object for PKCS#1 v1.5 OAEP. - Do not create directly: use :func:`new` instead.""" - - def __init__(self, key, hashAlgo, mgfunc, label, randfunc): - """Initialize this PKCS#1 OAEP cipher object. - - :Parameters: - key : an RSA key object - If a private half is given, both encryption and decryption are possible. - If a public half is given, only encryption is possible. - hashAlgo : hash object - The hash function to use. This can be a module under `Cryptodome.Hash` - or an existing hash object created from any of such modules. If not specified, - `Cryptodome.Hash.SHA1` is used. - mgfunc : callable - A mask generation function that accepts two parameters: a string to - use as seed, and the lenth of the mask to generate, in bytes. - If not specified, the standard MGF1 consistent with ``hashAlgo`` is used (a safe choice). - label : bytes/bytearray/memoryview - A label to apply to this particular encryption. If not specified, - an empty string is used. Specifying a label does not improve - security. - randfunc : callable - A function that returns random bytes. - - :attention: Modify the mask generation function only if you know what you are doing. - Sender and receiver must use the same one. - """ - self._key = key - - if hashAlgo: - self._hashObj = hashAlgo - else: - self._hashObj = Cryptodome.Hash.SHA1 - - if mgfunc: - self._mgf = mgfunc - else: - self._mgf = lambda x, y: MGF1(x, y, self._hashObj) - - self._label = _copy_bytes(None, None, label) - self._randfunc = randfunc - - def can_encrypt(self): - """Legacy function to check if you can call :meth:`encrypt`. - - .. deprecated:: 3.0""" - return self._key.can_encrypt() - - def can_decrypt(self): - """Legacy function to check if you can call :meth:`decrypt`. - - .. deprecated:: 3.0""" - return self._key.can_decrypt() - - def encrypt(self, message): - """Encrypt a message with PKCS#1 OAEP. - - :param message: - The message to encrypt, also known as plaintext. It can be of - variable length, but not longer than the RSA modulus (in bytes) - minus 2, minus twice the hash output size. - For instance, if you use RSA 2048 and SHA-256, the longest message - you can encrypt is 190 byte long. - :type message: bytes/bytearray/memoryview - - :returns: The ciphertext, as large as the RSA modulus. - :rtype: bytes - - :raises ValueError: - if the message is too long. - """ - - # See 7.1.1 in RFC3447 - modBits = Cryptodome.Util.number.size(self._key.n) - k = ceil_div(modBits, 8) # Convert from bits to bytes - hLen = self._hashObj.digest_size - mLen = len(message) - - # Step 1b - ps_len = k - mLen - 2 * hLen - 2 - if ps_len < 0: - raise ValueError("Plaintext is too long.") - # Step 2a - lHash = self._hashObj.new(self._label).digest() - # Step 2b - ps = b'\x00' * ps_len - # Step 2c - db = lHash + ps + b'\x01' + _copy_bytes(None, None, message) - # Step 2d - ros = self._randfunc(hLen) - # Step 2e - dbMask = self._mgf(ros, k-hLen-1) - # Step 2f - maskedDB = strxor(db, dbMask) - # Step 2g - seedMask = self._mgf(maskedDB, hLen) - # Step 2h - maskedSeed = strxor(ros, seedMask) - # Step 2i - em = b'\x00' + maskedSeed + maskedDB - # Step 3a (OS2IP) - em_int = bytes_to_long(em) - # Step 3b (RSAEP) - m_int = self._key._encrypt(em_int) - # Step 3c (I2OSP) - c = long_to_bytes(m_int, k) - return c - - def decrypt(self, ciphertext): - """Decrypt a message with PKCS#1 OAEP. - - :param ciphertext: The encrypted message. - :type ciphertext: bytes/bytearray/memoryview - - :returns: The original message (plaintext). - :rtype: bytes - - :raises ValueError: - if the ciphertext has the wrong length, or if decryption - fails the integrity check (in which case, the decryption - key is probably wrong). - :raises TypeError: - if the RSA key has no private half (i.e. you are trying - to decrypt using a public key). - """ - - # See 7.1.2 in RFC3447 - modBits = Cryptodome.Util.number.size(self._key.n) - k = ceil_div(modBits, 8) # Convert from bits to bytes - hLen = self._hashObj.digest_size - - # Step 1b and 1c - if len(ciphertext) != k or k < hLen+2: - raise ValueError("Ciphertext with incorrect length.") - # Step 2a (O2SIP) - ct_int = bytes_to_long(ciphertext) - # Step 2b (RSADP) and step 2c (I2OSP) - em = self._key._decrypt_to_bytes(ct_int) - # Step 3a - lHash = self._hashObj.new(self._label).digest() - # y must be 0, but we MUST NOT check it here in order not to - # allow attacks like Manger's (http://dl.acm.org/citation.cfm?id=704143) - maskedSeed = em[1:hLen+1] - maskedDB = em[hLen+1:] - # Step 3c - seedMask = self._mgf(maskedDB, hLen) - # Step 3d - seed = strxor(maskedSeed, seedMask) - # Step 3e - dbMask = self._mgf(seed, k-hLen-1) - # Step 3f - db = strxor(maskedDB, dbMask) - # Step 3b + 3g - res = oaep_decode(em, lHash, db) - if res <= 0: - raise ValueError("Incorrect decryption.") - # Step 4 - return db[res:] - - -def new(key, hashAlgo=None, mgfunc=None, label=b'', randfunc=None): - """Return a cipher object :class:`PKCS1OAEP_Cipher` - that can be used to perform PKCS#1 OAEP encryption or decryption. - - :param key: - The key object to use to encrypt or decrypt the message. - Decryption is only possible with a private RSA key. - :type key: RSA key object - - :param hashAlgo: - The hash function to use. This can be a module under `Cryptodome.Hash` - or an existing hash object created from any of such modules. - If not specified, `Cryptodome.Hash.SHA1` is used. - :type hashAlgo: hash object - - :param mgfunc: - A mask generation function that accepts two parameters: a string to - use as seed, and the lenth of the mask to generate, in bytes. - If not specified, the standard MGF1 consistent with ``hashAlgo`` is used (a safe choice). - :type mgfunc: callable - - :param label: - A label to apply to this particular encryption. If not specified, - an empty string is used. Specifying a label does not improve - security. - :type label: bytes/bytearray/memoryview - - :param randfunc: - A function that returns random bytes. - The default is `Random.get_random_bytes`. - :type randfunc: callable - """ - - if randfunc is None: - randfunc = Random.get_random_bytes - return PKCS1OAEP_Cipher(key, hashAlgo, mgfunc, label, randfunc) diff --git a/resources/lib/deps/Cryptodome/Cipher/PKCS1_OAEP.pyi b/resources/lib/deps/Cryptodome/Cipher/PKCS1_OAEP.pyi deleted file mode 100644 index b54cd3f..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/PKCS1_OAEP.pyi +++ /dev/null @@ -1,35 +0,0 @@ -from typing import Optional, Union, Callable, Any, overload -from typing_extensions import Protocol - -from Cryptodome.PublicKey.RSA import RsaKey - -class HashLikeClass(Protocol): - digest_size : int - def new(self, data: Optional[bytes] = ...) -> Any: ... - -class HashLikeModule(Protocol): - digest_size : int - @staticmethod - def new(data: Optional[bytes] = ...) -> Any: ... - -HashLike = Union[HashLikeClass, HashLikeModule] - -Buffer = Union[bytes, bytearray, memoryview] - -class PKCS1OAEP_Cipher: - def __init__(self, - key: RsaKey, - hashAlgo: HashLike, - mgfunc: Callable[[bytes, int], bytes], - label: Buffer, - randfunc: Callable[[int], bytes]) -> None: ... - def can_encrypt(self) -> bool: ... - def can_decrypt(self) -> bool: ... - def encrypt(self, message: Buffer) -> bytes: ... - def decrypt(self, ciphertext: Buffer) -> bytes: ... - -def new(key: RsaKey, - hashAlgo: Optional[HashLike] = ..., - mgfunc: Optional[Callable[[bytes, int], bytes]] = ..., - label: Optional[Buffer] = ..., - randfunc: Optional[Callable[[int], bytes]] = ...) -> PKCS1OAEP_Cipher: ... diff --git a/resources/lib/deps/Cryptodome/Cipher/PKCS1_v1_5.py b/resources/lib/deps/Cryptodome/Cipher/PKCS1_v1_5.py deleted file mode 100644 index d7a9b79..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/PKCS1_v1_5.py +++ /dev/null @@ -1,189 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Cipher/PKCS1-v1_5.py : PKCS#1 v1.5 -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -__all__ = ['new', 'PKCS115_Cipher'] - -from Cryptodome import Random -from Cryptodome.Util.number import bytes_to_long, long_to_bytes -from Cryptodome.Util.py3compat import bord, is_bytes, _copy_bytes -from ._pkcs1_oaep_decode import pkcs1_decode - - -class PKCS115_Cipher: - """This cipher can perform PKCS#1 v1.5 RSA encryption or decryption. - Do not instantiate directly. Use :func:`Cryptodome.Cipher.PKCS1_v1_5.new` instead.""" - - def __init__(self, key, randfunc): - """Initialize this PKCS#1 v1.5 cipher object. - - :Parameters: - key : an RSA key object - If a private half is given, both encryption and decryption are possible. - If a public half is given, only encryption is possible. - randfunc : callable - Function that returns random bytes. - """ - - self._key = key - self._randfunc = randfunc - - def can_encrypt(self): - """Return True if this cipher object can be used for encryption.""" - return self._key.can_encrypt() - - def can_decrypt(self): - """Return True if this cipher object can be used for decryption.""" - return self._key.can_decrypt() - - def encrypt(self, message): - """Produce the PKCS#1 v1.5 encryption of a message. - - This function is named ``RSAES-PKCS1-V1_5-ENCRYPT``, and it is specified in - `section 7.2.1 of RFC8017 - `_. - - :param message: - The message to encrypt, also known as plaintext. It can be of - variable length, but not longer than the RSA modulus (in bytes) minus 11. - :type message: bytes/bytearray/memoryview - - :Returns: A byte string, the ciphertext in which the message is encrypted. - It is as long as the RSA modulus (in bytes). - - :Raises ValueError: - If the RSA key length is not sufficiently long to deal with the given - message. - """ - - # See 7.2.1 in RFC8017 - k = self._key.size_in_bytes() - mLen = len(message) - - # Step 1 - if mLen > k - 11: - raise ValueError("Plaintext is too long.") - # Step 2a - ps = [] - while len(ps) != k - mLen - 3: - new_byte = self._randfunc(1) - if bord(new_byte[0]) == 0x00: - continue - ps.append(new_byte) - ps = b"".join(ps) - # Step 2b - em = b'\x00\x02' + ps + b'\x00' + _copy_bytes(None, None, message) - # Step 3a (OS2IP) - em_int = bytes_to_long(em) - # Step 3b (RSAEP) - m_int = self._key._encrypt(em_int) - # Step 3c (I2OSP) - c = long_to_bytes(m_int, k) - return c - - def decrypt(self, ciphertext, sentinel, expected_pt_len=0): - r"""Decrypt a PKCS#1 v1.5 ciphertext. - - This is the function ``RSAES-PKCS1-V1_5-DECRYPT`` specified in - `section 7.2.2 of RFC8017 - `_. - - Args: - ciphertext (bytes/bytearray/memoryview): - The ciphertext that contains the message to recover. - sentinel (any type): - The object to return whenever an error is detected. - expected_pt_len (integer): - The length the plaintext is known to have, or 0 if unknown. - - Returns (byte string): - It is either the original message or the ``sentinel`` (in case of an error). - - .. warning:: - PKCS#1 v1.5 decryption is intrinsically vulnerable to timing - attacks (see `Bleichenbacher's`__ attack). - **Use PKCS#1 OAEP instead**. - - This implementation attempts to mitigate the risk - with some constant-time constructs. - However, they are not sufficient by themselves: the type of protocol you - implement and the way you handle errors make a big difference. - - Specifically, you should make it very hard for the (malicious) - party that submitted the ciphertext to quickly understand if decryption - succeeded or not. - - To this end, it is recommended that your protocol only encrypts - plaintexts of fixed length (``expected_pt_len``), - that ``sentinel`` is a random byte string of the same length, - and that processing continues for as long - as possible even if ``sentinel`` is returned (i.e. in case of - incorrect decryption). - - .. __: https://dx.doi.org/10.1007/BFb0055716 - """ - - # See 7.2.2 in RFC8017 - k = self._key.size_in_bytes() - - # Step 1 - if len(ciphertext) != k: - raise ValueError("Ciphertext with incorrect length (not %d bytes)" % k) - - # Step 2a (O2SIP) - ct_int = bytes_to_long(ciphertext) - - # Step 2b (RSADP) and Step 2c (I2OSP) - em = self._key._decrypt_to_bytes(ct_int) - - # Step 3 (not constant time when the sentinel is not a byte string) - output = bytes(bytearray(k)) - if not is_bytes(sentinel) or len(sentinel) > k: - size = pkcs1_decode(em, b'', expected_pt_len, output) - if size < 0: - return sentinel - else: - return output[size:] - - # Step 3 (somewhat constant time) - size = pkcs1_decode(em, sentinel, expected_pt_len, output) - return output[size:] - - -def new(key, randfunc=None): - """Create a cipher for performing PKCS#1 v1.5 encryption or decryption. - - :param key: - The key to use to encrypt or decrypt the message. This is a `Cryptodome.PublicKey.RSA` object. - Decryption is only possible if *key* is a private RSA key. - :type key: RSA key object - - :param randfunc: - Function that return random bytes. - The default is :func:`Cryptodome.Random.get_random_bytes`. - :type randfunc: callable - - :returns: A cipher object `PKCS115_Cipher`. - """ - - if randfunc is None: - randfunc = Random.get_random_bytes - return PKCS115_Cipher(key, randfunc) diff --git a/resources/lib/deps/Cryptodome/Cipher/PKCS1_v1_5.pyi b/resources/lib/deps/Cryptodome/Cipher/PKCS1_v1_5.pyi deleted file mode 100644 index b69f509..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/PKCS1_v1_5.pyi +++ /dev/null @@ -1,20 +0,0 @@ -from typing import Callable, Union, Any, Optional, TypeVar - -from Cryptodome.PublicKey.RSA import RsaKey - -Buffer = Union[bytes, bytearray, memoryview] -T = TypeVar('T') - -class PKCS115_Cipher: - def __init__(self, - key: RsaKey, - randfunc: Callable[[int], bytes]) -> None: ... - def can_encrypt(self) -> bool: ... - def can_decrypt(self) -> bool: ... - def encrypt(self, message: Buffer) -> bytes: ... - def decrypt(self, ciphertext: Buffer, - sentinel: T, - expected_pt_len: Optional[int] = ...) -> Union[bytes, T]: ... - -def new(key: RsaKey, - randfunc: Optional[Callable[[int], bytes]] = ...) -> PKCS115_Cipher: ... diff --git a/resources/lib/deps/Cryptodome/Cipher/Salsa20.py b/resources/lib/deps/Cryptodome/Cipher/Salsa20.py deleted file mode 100644 index 79e6701..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/Salsa20.py +++ /dev/null @@ -1,167 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Cipher/Salsa20.py : Salsa20 stream cipher (http://cr.yp.to/snuffle.html) -# -# Contributed by Fabrizio Tarizzo . -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -from Cryptodome.Util.py3compat import _copy_bytes -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - create_string_buffer, - get_raw_buffer, VoidPointer, - SmartPointer, c_size_t, - c_uint8_ptr, is_writeable_buffer) - -from Cryptodome.Random import get_random_bytes - -_raw_salsa20_lib = load_pycryptodome_raw_lib("Cryptodome.Cipher._Salsa20", - """ - int Salsa20_stream_init(uint8_t *key, size_t keylen, - uint8_t *nonce, size_t nonce_len, - void **pSalsaState); - int Salsa20_stream_destroy(void *salsaState); - int Salsa20_stream_encrypt(void *salsaState, - const uint8_t in[], - uint8_t out[], size_t len); - """) - - -class Salsa20Cipher: - """Salsa20 cipher object. Do not create it directly. Use :py:func:`new` - instead. - - :var nonce: The nonce with length 8 - :vartype nonce: byte string - """ - - def __init__(self, key, nonce): - """Initialize a Salsa20 cipher object - - See also `new()` at the module level.""" - - if len(key) not in key_size: - raise ValueError("Incorrect key length for Salsa20 (%d bytes)" % len(key)) - - if len(nonce) != 8: - raise ValueError("Incorrect nonce length for Salsa20 (%d bytes)" % - len(nonce)) - - self.nonce = _copy_bytes(None, None, nonce) - - self._state = VoidPointer() - result = _raw_salsa20_lib.Salsa20_stream_init( - c_uint8_ptr(key), - c_size_t(len(key)), - c_uint8_ptr(nonce), - c_size_t(len(nonce)), - self._state.address_of()) - if result: - raise ValueError("Error %d instantiating a Salsa20 cipher") - self._state = SmartPointer(self._state.get(), - _raw_salsa20_lib.Salsa20_stream_destroy) - - self.block_size = 1 - self.key_size = len(key) - - def encrypt(self, plaintext, output=None): - """Encrypt a piece of data. - - Args: - plaintext(bytes/bytearray/memoryview): The data to encrypt, of any size. - Keyword Args: - output(bytes/bytearray/memoryview): The location where the ciphertext - is written to. If ``None``, the ciphertext is returned. - Returns: - If ``output`` is ``None``, the ciphertext is returned as ``bytes``. - Otherwise, ``None``. - """ - - if output is None: - ciphertext = create_string_buffer(len(plaintext)) - else: - ciphertext = output - - if not is_writeable_buffer(output): - raise TypeError("output must be a bytearray or a writeable memoryview") - - if len(plaintext) != len(output): - raise ValueError("output must have the same length as the input" - " (%d bytes)" % len(plaintext)) - - result = _raw_salsa20_lib.Salsa20_stream_encrypt( - self._state.get(), - c_uint8_ptr(plaintext), - c_uint8_ptr(ciphertext), - c_size_t(len(plaintext))) - if result: - raise ValueError("Error %d while encrypting with Salsa20" % result) - - if output is None: - return get_raw_buffer(ciphertext) - else: - return None - - def decrypt(self, ciphertext, output=None): - """Decrypt a piece of data. - - Args: - ciphertext(bytes/bytearray/memoryview): The data to decrypt, of any size. - Keyword Args: - output(bytes/bytearray/memoryview): The location where the plaintext - is written to. If ``None``, the plaintext is returned. - Returns: - If ``output`` is ``None``, the plaintext is returned as ``bytes``. - Otherwise, ``None``. - """ - - try: - return self.encrypt(ciphertext, output=output) - except ValueError as e: - raise ValueError(str(e).replace("enc", "dec")) - - -def new(key, nonce=None): - """Create a new Salsa20 cipher - - :keyword key: The secret key to use. It must be 16 or 32 bytes long. - :type key: bytes/bytearray/memoryview - - :keyword nonce: - A value that must never be reused for any other encryption - done with this key. It must be 8 bytes long. - - If not provided, a random byte string will be generated (you can read - it back via the ``nonce`` attribute of the returned object). - :type nonce: bytes/bytearray/memoryview - - :Return: a :class:`Cryptodome.Cipher.Salsa20.Salsa20Cipher` object - """ - - if nonce is None: - nonce = get_random_bytes(8) - - return Salsa20Cipher(key, nonce) - -# Size of a data block (in bytes) -block_size = 1 - -# Size of a key (in bytes) -key_size = (16, 32) - diff --git a/resources/lib/deps/Cryptodome/Cipher/Salsa20.pyi b/resources/lib/deps/Cryptodome/Cipher/Salsa20.pyi deleted file mode 100644 index cf8690e..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/Salsa20.pyi +++ /dev/null @@ -1,26 +0,0 @@ -from typing import Union, Tuple, Optional, overload, Optional - -Buffer = bytes|bytearray|memoryview - -class Salsa20Cipher: - nonce: bytes - block_size: int - key_size: int - - def __init__(self, - key: Buffer, - nonce: Buffer) -> None: ... - @overload - def encrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def encrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - @overload - def decrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def decrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - -def new(key: Buffer, nonce: Optional[Buffer] = ...) -> Salsa20Cipher: ... - -block_size: int -key_size: Tuple[int, int] - diff --git a/resources/lib/deps/Cryptodome/Cipher/_ARC4.abi3.so b/resources/lib/deps/Cryptodome/Cipher/_ARC4.abi3.so deleted file mode 100755 index 451d359707bb1f98ab6c8a231f3eeaa98b7cc774..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21016 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 CAhy;3 diff --git a/resources/lib/deps/Cryptodome/Cipher/_EKSBlowfish.py b/resources/lib/deps/Cryptodome/Cipher/_EKSBlowfish.py deleted file mode 100644 index c1c3249..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_EKSBlowfish.py +++ /dev/null @@ -1,131 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2019, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import sys - -from Cryptodome.Cipher import _create_cipher -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, c_size_t, - c_uint8_ptr, c_uint) - -_raw_blowfish_lib = load_pycryptodome_raw_lib( - "Cryptodome.Cipher._raw_eksblowfish", - """ - int EKSBlowfish_start_operation(const uint8_t key[], - size_t key_len, - const uint8_t salt[16], - size_t salt_len, - unsigned cost, - unsigned invert, - void **pResult); - int EKSBlowfish_encrypt(const void *state, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int EKSBlowfish_decrypt(const void *state, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int EKSBlowfish_stop_operation(void *state); - """ - ) - - -def _create_base_cipher(dict_parameters): - """This method instantiates and returns a smart pointer to - a low-level base cipher. It will absorb named parameters in - the process.""" - - try: - key = dict_parameters.pop("key") - salt = dict_parameters.pop("salt") - cost = dict_parameters.pop("cost") - except KeyError as e: - raise TypeError("Missing EKSBlowfish parameter: " + str(e)) - invert = dict_parameters.pop("invert", True) - - if len(key) not in key_size: - raise ValueError("Incorrect EKSBlowfish key length (%d bytes)" % len(key)) - - start_operation = _raw_blowfish_lib.EKSBlowfish_start_operation - stop_operation = _raw_blowfish_lib.EKSBlowfish_stop_operation - - void_p = VoidPointer() - result = start_operation(c_uint8_ptr(key), - c_size_t(len(key)), - c_uint8_ptr(salt), - c_size_t(len(salt)), - c_uint(cost), - c_uint(int(invert)), - void_p.address_of()) - if result: - raise ValueError("Error %X while instantiating the EKSBlowfish cipher" - % result) - return SmartPointer(void_p.get(), stop_operation) - - -def new(key, mode, salt, cost, invert): - """Create a new EKSBlowfish cipher - - Args: - - key (bytes, bytearray, memoryview): - The secret key to use in the symmetric cipher. - Its length can vary from 0 to 72 bytes. - - mode (one of the supported ``MODE_*`` constants): - The chaining mode to use for encryption or decryption. - - salt (bytes, bytearray, memoryview): - The salt that bcrypt uses to thwart rainbow table attacks - - cost (integer): - The complexity factor in bcrypt - - invert (bool): - If ``False``, in the inner loop use ``ExpandKey`` first over the salt - and then over the key, as defined in - the `original bcrypt specification `_. - If ``True``, reverse the order, as in the first implementation of - `bcrypt` in OpenBSD. - - :Return: an EKSBlowfish object - """ - - kwargs = { 'salt':salt, 'cost':cost, 'invert':invert } - return _create_cipher(sys.modules[__name__], key, mode, **kwargs) - - -MODE_ECB = 1 - -# Size of a data block (in bytes) -block_size = 8 -# Size of a key (in bytes) -key_size = range(0, 72 + 1) diff --git a/resources/lib/deps/Cryptodome/Cipher/_EKSBlowfish.pyi b/resources/lib/deps/Cryptodome/Cipher/_EKSBlowfish.pyi deleted file mode 100644 index 49c8448..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_EKSBlowfish.pyi +++ /dev/null @@ -1,15 +0,0 @@ -from typing import Union, Iterable - -from Cryptodome.Cipher._mode_ecb import EcbMode - -MODE_ECB: int - -Buffer = Union[bytes, bytearray, memoryview] - -def new(key: Buffer, - mode: int, - salt: Buffer, - cost: int) -> EcbMode: ... - -block_size: int -key_size: Iterable[int] diff --git a/resources/lib/deps/Cryptodome/Cipher/_Salsa20.abi3.so b/resources/lib/deps/Cryptodome/Cipher/_Salsa20.abi3.so deleted file mode 100755 index a303d911a4b77471b48fa845c0ded098329a5318..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27016 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 base_cipher_state -# - AES_encrypt(base_cipher_state, in, out, length) -# - AES_decrypt(base_cipher_state, in, out, length) -# - AES_stop_operation(base_cipher_state) -# -# Where base_cipher_state is AES_State, a struct with BlockBase (set of -# pointers to encrypt/decrypt/stop) followed by cipher-specific data. -# -# The API of #2 is (replace "CBC" with the name of the actual mode): -# - CBC_start_operation(base_cipher_state) --> mode_state -# - CBC_encrypt(mode_state, in, out, length) -# - CBC_decrypt(mode_state, in, out, length) -# - CBC_stop_operation(mode_state) -# -# where mode_state is a a pointer to base_cipher_state plus mode-specific data. - -import os - -from Cryptodome.Cipher._mode_ecb import _create_ecb_cipher -from Cryptodome.Cipher._mode_cbc import _create_cbc_cipher -from Cryptodome.Cipher._mode_cfb import _create_cfb_cipher -from Cryptodome.Cipher._mode_ofb import _create_ofb_cipher -from Cryptodome.Cipher._mode_ctr import _create_ctr_cipher -from Cryptodome.Cipher._mode_openpgp import _create_openpgp_cipher -from Cryptodome.Cipher._mode_ccm import _create_ccm_cipher -from Cryptodome.Cipher._mode_eax import _create_eax_cipher -from Cryptodome.Cipher._mode_siv import _create_siv_cipher -from Cryptodome.Cipher._mode_gcm import _create_gcm_cipher -from Cryptodome.Cipher._mode_ocb import _create_ocb_cipher - -_modes = { 1:_create_ecb_cipher, - 2:_create_cbc_cipher, - 3:_create_cfb_cipher, - 5:_create_ofb_cipher, - 6:_create_ctr_cipher, - 7:_create_openpgp_cipher, - 9:_create_eax_cipher - } - -_extra_modes = { 8:_create_ccm_cipher, - 10:_create_siv_cipher, - 11:_create_gcm_cipher, - 12:_create_ocb_cipher - } - -def _create_cipher(factory, key, mode, *args, **kwargs): - - kwargs["key"] = key - - modes = dict(_modes) - if kwargs.pop("add_aes_modes", False): - modes.update(_extra_modes) - if not mode in modes: - raise ValueError("Mode not supported") - - if args: - if mode in (8, 9, 10, 11, 12): - if len(args) > 1: - raise TypeError("Too many arguments for this mode") - kwargs["nonce"] = args[0] - elif mode in (2, 3, 5, 7): - if len(args) > 1: - raise TypeError("Too many arguments for this mode") - kwargs["IV"] = args[0] - elif mode == 6: - if len(args) > 0: - raise TypeError("Too many arguments for this mode") - elif mode == 1: - raise TypeError("IV is not meaningful for the ECB mode") - - return modes[mode](factory, **kwargs) diff --git a/resources/lib/deps/Cryptodome/Cipher/__init__.pyi b/resources/lib/deps/Cryptodome/Cipher/__init__.pyi deleted file mode 100644 index e69de29..0000000 diff --git a/resources/lib/deps/Cryptodome/Cipher/_chacha20.abi3.so b/resources/lib/deps/Cryptodome/Cipher/_chacha20.abi3.so deleted file mode 100755 index f1f1fa1e46a37331db3d5b605415fe31b383af57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30624 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 f9$0HRwb5jMXKT -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -""" -Ciphertext Block Chaining (CBC) mode. -""" - -__all__ = ['CbcMode'] - -from Cryptodome.Util.py3compat import _copy_bytes -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, - create_string_buffer, get_raw_buffer, - SmartPointer, c_size_t, c_uint8_ptr, - is_writeable_buffer) - -from Cryptodome.Random import get_random_bytes - -raw_cbc_lib = load_pycryptodome_raw_lib("Cryptodome.Cipher._raw_cbc", """ - int CBC_start_operation(void *cipher, - const uint8_t iv[], - size_t iv_len, - void **pResult); - int CBC_encrypt(void *cbcState, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int CBC_decrypt(void *cbcState, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int CBC_stop_operation(void *state); - """ - ) - - -class CbcMode(object): - """*Cipher-Block Chaining (CBC)*. - - Each of the ciphertext blocks depends on the current - and all previous plaintext blocks. - - An Initialization Vector (*IV*) is required. - - See `NIST SP800-38A`_ , Section 6.2 . - - .. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf - - :undocumented: __init__ - """ - - def __init__(self, block_cipher, iv): - """Create a new block cipher, configured in CBC mode. - - :Parameters: - block_cipher : C pointer - A smart pointer to the low-level block cipher instance. - - iv : bytes/bytearray/memoryview - The initialization vector to use for encryption or decryption. - It is as long as the cipher block. - - **The IV must be unpredictable**. Ideally it is picked randomly. - - Reusing the *IV* for encryptions performed with the same key - compromises confidentiality. - """ - - self._state = VoidPointer() - result = raw_cbc_lib.CBC_start_operation(block_cipher.get(), - c_uint8_ptr(iv), - c_size_t(len(iv)), - self._state.address_of()) - if result: - raise ValueError("Error %d while instantiating the CBC mode" - % result) - - # Ensure that object disposal of this Python object will (eventually) - # free the memory allocated by the raw library for the cipher mode - self._state = SmartPointer(self._state.get(), - raw_cbc_lib.CBC_stop_operation) - - # Memory allocated for the underlying block cipher is now owed - # by the cipher mode - block_cipher.release() - - self.block_size = len(iv) - """The block size of the underlying cipher, in bytes.""" - - self.iv = _copy_bytes(None, None, iv) - """The Initialization Vector originally used to create the object. - The value does not change.""" - - self.IV = self.iv - """Alias for `iv`""" - - self._next = ["encrypt", "decrypt"] - - def encrypt(self, plaintext, output=None): - """Encrypt data with the key and the parameters set at initialization. - - A cipher object is stateful: once you have encrypted a message - you cannot encrypt (or decrypt) another message using the same - object. - - The data to encrypt can be broken up in two or - more pieces and `encrypt` can be called multiple times. - - That is, the statement: - - >>> c.encrypt(a) + c.encrypt(b) - - is equivalent to: - - >>> c.encrypt(a+b) - - That also means that you cannot reuse an object for encrypting - or decrypting other data with the same key. - - This function does not add any padding to the plaintext. - - :Parameters: - plaintext : bytes/bytearray/memoryview - The piece of data to encrypt. - Its lenght must be multiple of the cipher block size. - :Keywords: - output : bytearray/memoryview - The location where the ciphertext must be written to. - If ``None``, the ciphertext is returned. - :Return: - If ``output`` is ``None``, the ciphertext is returned as ``bytes``. - Otherwise, ``None``. - """ - - if "encrypt" not in self._next: - raise TypeError("encrypt() cannot be called after decrypt()") - self._next = ["encrypt"] - - if output is None: - ciphertext = create_string_buffer(len(plaintext)) - else: - ciphertext = output - - if not is_writeable_buffer(output): - raise TypeError("output must be a bytearray or a writeable memoryview") - - if len(plaintext) != len(output): - raise ValueError("output must have the same length as the input" - " (%d bytes)" % len(plaintext)) - - result = raw_cbc_lib.CBC_encrypt(self._state.get(), - c_uint8_ptr(plaintext), - c_uint8_ptr(ciphertext), - c_size_t(len(plaintext))) - if result: - if result == 3: - raise ValueError("Data must be padded to %d byte boundary in CBC mode" % self.block_size) - raise ValueError("Error %d while encrypting in CBC mode" % result) - - if output is None: - return get_raw_buffer(ciphertext) - else: - return None - - def decrypt(self, ciphertext, output=None): - """Decrypt data with the key and the parameters set at initialization. - - A cipher object is stateful: once you have decrypted a message - you cannot decrypt (or encrypt) another message with the same - object. - - The data to decrypt can be broken up in two or - more pieces and `decrypt` can be called multiple times. - - That is, the statement: - - >>> c.decrypt(a) + c.decrypt(b) - - is equivalent to: - - >>> c.decrypt(a+b) - - This function does not remove any padding from the plaintext. - - :Parameters: - ciphertext : bytes/bytearray/memoryview - The piece of data to decrypt. - Its length must be multiple of the cipher block size. - :Keywords: - output : bytearray/memoryview - The location where the plaintext must be written to. - If ``None``, the plaintext is returned. - :Return: - If ``output`` is ``None``, the plaintext is returned as ``bytes``. - Otherwise, ``None``. - """ - - if "decrypt" not in self._next: - raise TypeError("decrypt() cannot be called after encrypt()") - self._next = ["decrypt"] - - if output is None: - plaintext = create_string_buffer(len(ciphertext)) - else: - plaintext = output - - if not is_writeable_buffer(output): - raise TypeError("output must be a bytearray or a writeable memoryview") - - if len(ciphertext) != len(output): - raise ValueError("output must have the same length as the input" - " (%d bytes)" % len(plaintext)) - - result = raw_cbc_lib.CBC_decrypt(self._state.get(), - c_uint8_ptr(ciphertext), - c_uint8_ptr(plaintext), - c_size_t(len(ciphertext))) - if result: - if result == 3: - raise ValueError("Data must be padded to %d byte boundary in CBC mode" % self.block_size) - raise ValueError("Error %d while decrypting in CBC mode" % result) - - if output is None: - return get_raw_buffer(plaintext) - else: - return None - - -def _create_cbc_cipher(factory, **kwargs): - """Instantiate a cipher object that performs CBC encryption/decryption. - - :Parameters: - factory : module - The underlying block cipher, a module from ``Cryptodome.Cipher``. - - :Keywords: - iv : bytes/bytearray/memoryview - The IV to use for CBC. - - IV : bytes/bytearray/memoryview - Alias for ``iv``. - - Any other keyword will be passed to the underlying block cipher. - See the relevant documentation for details (at least ``key`` will need - to be present). - """ - - cipher_state = factory._create_base_cipher(kwargs) - iv = kwargs.pop("IV", None) - IV = kwargs.pop("iv", None) - - if (None, None) == (iv, IV): - iv = get_random_bytes(factory.block_size) - if iv is not None: - if IV is not None: - raise TypeError("You must either use 'iv' or 'IV', not both") - else: - iv = IV - - if len(iv) != factory.block_size: - raise ValueError("Incorrect IV length (it must be %d bytes long)" % - factory.block_size) - - if kwargs: - raise TypeError("Unknown parameters for CBC: %s" % str(kwargs)) - - return CbcMode(cipher_state, iv) diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_cbc.pyi b/resources/lib/deps/Cryptodome/Cipher/_mode_cbc.pyi deleted file mode 100644 index 526632e..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_cbc.pyi +++ /dev/null @@ -1,25 +0,0 @@ -from typing import Union, overload - -from Cryptodome.Util._raw_api import SmartPointer - -Buffer = Union[bytes, bytearray, memoryview] - -__all__ = ['CbcMode'] - -class CbcMode(object): - block_size: int - iv: Buffer - IV: Buffer - - def __init__(self, - block_cipher: SmartPointer, - iv: Buffer) -> None: ... - @overload - def encrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def encrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - @overload - def decrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def decrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_ccm.py b/resources/lib/deps/Cryptodome/Cipher/_mode_ccm.py deleted file mode 100644 index ec2e4f4..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_ccm.py +++ /dev/null @@ -1,650 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -""" -Counter with CBC-MAC (CCM) mode. -""" - -__all__ = ['CcmMode'] - -import struct -from binascii import unhexlify - -from Cryptodome.Util.py3compat import (byte_string, bord, - _copy_bytes) -from Cryptodome.Util._raw_api import is_writeable_buffer - -from Cryptodome.Util.strxor import strxor -from Cryptodome.Util.number import long_to_bytes - -from Cryptodome.Hash import BLAKE2s -from Cryptodome.Random import get_random_bytes - - -def enum(**enums): - return type('Enum', (), enums) - -MacStatus = enum(NOT_STARTED=0, PROCESSING_AUTH_DATA=1, PROCESSING_PLAINTEXT=2) - - -class CcmMode(object): - """Counter with CBC-MAC (CCM). - - This is an Authenticated Encryption with Associated Data (`AEAD`_) mode. - It provides both confidentiality and authenticity. - - The header of the message may be left in the clear, if needed, and it will - still be subject to authentication. The decryption step tells the receiver - if the message comes from a source that really knowns the secret key. - Additionally, decryption detects if any part of the message - including the - header - has been modified or corrupted. - - This mode requires a nonce. The nonce shall never repeat for two - different messages encrypted with the same key, but it does not need - to be random. - Note that there is a trade-off between the size of the nonce and the - maximum size of a single message you can encrypt. - - It is important to use a large nonce if the key is reused across several - messages and the nonce is chosen randomly. - - It is acceptable to us a short nonce if the key is only used a few times or - if the nonce is taken from a counter. - - The following table shows the trade-off when the nonce is chosen at - random. The column on the left shows how many messages it takes - for the keystream to repeat **on average**. In practice, you will want to - stop using the key way before that. - - +--------------------+---------------+-------------------+ - | Avg. # of messages | nonce | Max. message | - | before keystream | size | size | - | repeats | (bytes) | (bytes) | - +====================+===============+===================+ - | 2^52 | 13 | 64K | - +--------------------+---------------+-------------------+ - | 2^48 | 12 | 16M | - +--------------------+---------------+-------------------+ - | 2^44 | 11 | 4G | - +--------------------+---------------+-------------------+ - | 2^40 | 10 | 1T | - +--------------------+---------------+-------------------+ - | 2^36 | 9 | 64P | - +--------------------+---------------+-------------------+ - | 2^32 | 8 | 16E | - +--------------------+---------------+-------------------+ - - This mode is only available for ciphers that operate on 128 bits blocks - (e.g. AES but not TDES). - - See `NIST SP800-38C`_ or RFC3610_. - - .. _`NIST SP800-38C`: http://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C.pdf - .. _RFC3610: https://tools.ietf.org/html/rfc3610 - .. _AEAD: http://blog.cryptographyengineering.com/2012/05/how-to-choose-authenticated-encryption.html - - :undocumented: __init__ - """ - - def __init__(self, factory, key, nonce, mac_len, msg_len, assoc_len, - cipher_params): - - self.block_size = factory.block_size - """The block size of the underlying cipher, in bytes.""" - - self.nonce = _copy_bytes(None, None, nonce) - """The nonce used for this cipher instance""" - - self._factory = factory - self._key = _copy_bytes(None, None, key) - self._mac_len = mac_len - self._msg_len = msg_len - self._assoc_len = assoc_len - self._cipher_params = cipher_params - - self._mac_tag = None # Cache for MAC tag - - if self.block_size != 16: - raise ValueError("CCM mode is only available for ciphers" - " that operate on 128 bits blocks") - - # MAC tag length (Tlen) - if mac_len not in (4, 6, 8, 10, 12, 14, 16): - raise ValueError("Parameter 'mac_len' must be even" - " and in the range 4..16 (not %d)" % mac_len) - - # Nonce value - if not (nonce and 7 <= len(nonce) <= 13): - raise ValueError("Length of parameter 'nonce' must be" - " in the range 7..13 bytes") - - # Create MAC object (the tag will be the last block - # bytes worth of ciphertext) - self._mac = self._factory.new(key, - factory.MODE_CBC, - iv=b'\x00' * 16, - **cipher_params) - self._mac_status = MacStatus.NOT_STARTED - self._t = None - - # Allowed transitions after initialization - self._next = ["update", "encrypt", "decrypt", - "digest", "verify"] - - # Cumulative lengths - self._cumul_assoc_len = 0 - self._cumul_msg_len = 0 - - # Cache for unaligned associated data/plaintext. - # This is a list with byte strings, but when the MAC starts, - # it will become a binary string no longer than the block size. - self._cache = [] - - # Start CTR cipher, by formatting the counter (A.3) - q = 15 - len(nonce) # length of Q, the encoded message length - self._cipher = self._factory.new(key, - self._factory.MODE_CTR, - nonce=struct.pack("B", q - 1) + self.nonce, - **cipher_params) - - # S_0, step 6 in 6.1 for j=0 - self._s_0 = self._cipher.encrypt(b'\x00' * 16) - - # Try to start the MAC - if None not in (assoc_len, msg_len): - self._start_mac() - - def _start_mac(self): - - assert(self._mac_status == MacStatus.NOT_STARTED) - assert(None not in (self._assoc_len, self._msg_len)) - assert(isinstance(self._cache, list)) - - # Formatting control information and nonce (A.2.1) - q = 15 - len(self.nonce) # length of Q, the encoded message length - flags = (64 * (self._assoc_len > 0) + 8 * ((self._mac_len - 2) // 2) + - (q - 1)) - b_0 = struct.pack("B", flags) + self.nonce + long_to_bytes(self._msg_len, q) - - # Formatting associated data (A.2.2) - # Encoded 'a' is concatenated with the associated data 'A' - assoc_len_encoded = b'' - if self._assoc_len > 0: - if self._assoc_len < (2 ** 16 - 2 ** 8): - enc_size = 2 - elif self._assoc_len < (2 ** 32): - assoc_len_encoded = b'\xFF\xFE' - enc_size = 4 - else: - assoc_len_encoded = b'\xFF\xFF' - enc_size = 8 - assoc_len_encoded += long_to_bytes(self._assoc_len, enc_size) - - # b_0 and assoc_len_encoded must be processed first - self._cache.insert(0, b_0) - self._cache.insert(1, assoc_len_encoded) - - # Process all the data cached so far - first_data_to_mac = b"".join(self._cache) - self._cache = b"" - self._mac_status = MacStatus.PROCESSING_AUTH_DATA - self._update(first_data_to_mac) - - def _pad_cache_and_update(self): - - assert(self._mac_status != MacStatus.NOT_STARTED) - assert(len(self._cache) < self.block_size) - - # Associated data is concatenated with the least number - # of zero bytes (possibly none) to reach alignment to - # the 16 byte boundary (A.2.3) - len_cache = len(self._cache) - if len_cache > 0: - self._update(b'\x00' * (self.block_size - len_cache)) - - def update(self, assoc_data): - """Protect associated data - - If there is any associated data, the caller has to invoke - this function one or more times, before using - ``decrypt`` or ``encrypt``. - - By *associated data* it is meant any data (e.g. packet headers) that - will not be encrypted and will be transmitted in the clear. - However, the receiver is still able to detect any modification to it. - In CCM, the *associated data* is also called - *additional authenticated data* (AAD). - - If there is no associated data, this method must not be called. - - The caller may split associated data in segments of any size, and - invoke this method multiple times, each time with the next segment. - - :Parameters: - assoc_data : bytes/bytearray/memoryview - A piece of associated data. There are no restrictions on its size. - """ - - if "update" not in self._next: - raise TypeError("update() can only be called" - " immediately after initialization") - - self._next = ["update", "encrypt", "decrypt", - "digest", "verify"] - - self._cumul_assoc_len += len(assoc_data) - if self._assoc_len is not None and \ - self._cumul_assoc_len > self._assoc_len: - raise ValueError("Associated data is too long") - - self._update(assoc_data) - return self - - def _update(self, assoc_data_pt=b""): - """Update the MAC with associated data or plaintext - (without FSM checks)""" - - # If MAC has not started yet, we just park the data into a list. - # If the data is mutable, we create a copy and store that instead. - if self._mac_status == MacStatus.NOT_STARTED: - if is_writeable_buffer(assoc_data_pt): - assoc_data_pt = _copy_bytes(None, None, assoc_data_pt) - self._cache.append(assoc_data_pt) - return - - assert(len(self._cache) < self.block_size) - - if len(self._cache) > 0: - filler = min(self.block_size - len(self._cache), - len(assoc_data_pt)) - self._cache += _copy_bytes(None, filler, assoc_data_pt) - assoc_data_pt = _copy_bytes(filler, None, assoc_data_pt) - - if len(self._cache) < self.block_size: - return - - # The cache is exactly one block - self._t = self._mac.encrypt(self._cache) - self._cache = b"" - - update_len = len(assoc_data_pt) // self.block_size * self.block_size - self._cache = _copy_bytes(update_len, None, assoc_data_pt) - if update_len > 0: - self._t = self._mac.encrypt(assoc_data_pt[:update_len])[-16:] - - def encrypt(self, plaintext, output=None): - """Encrypt data with the key set at initialization. - - A cipher object is stateful: once you have encrypted a message - you cannot encrypt (or decrypt) another message using the same - object. - - This method can be called only **once** if ``msg_len`` was - not passed at initialization. - - If ``msg_len`` was given, the data to encrypt can be broken - up in two or more pieces and `encrypt` can be called - multiple times. - - That is, the statement: - - >>> c.encrypt(a) + c.encrypt(b) - - is equivalent to: - - >>> c.encrypt(a+b) - - This function does not add any padding to the plaintext. - - :Parameters: - plaintext : bytes/bytearray/memoryview - The piece of data to encrypt. - It can be of any length. - :Keywords: - output : bytearray/memoryview - The location where the ciphertext must be written to. - If ``None``, the ciphertext is returned. - :Return: - If ``output`` is ``None``, the ciphertext as ``bytes``. - Otherwise, ``None``. - """ - - if "encrypt" not in self._next: - raise TypeError("encrypt() can only be called after" - " initialization or an update()") - self._next = ["encrypt", "digest"] - - # No more associated data allowed from now - if self._assoc_len is None: - assert(isinstance(self._cache, list)) - self._assoc_len = sum([len(x) for x in self._cache]) - if self._msg_len is not None: - self._start_mac() - else: - if self._cumul_assoc_len < self._assoc_len: - raise ValueError("Associated data is too short") - - # Only once piece of plaintext accepted if message length was - # not declared in advance - if self._msg_len is None: - self._msg_len = len(plaintext) - self._start_mac() - self._next = ["digest"] - - self._cumul_msg_len += len(plaintext) - if self._cumul_msg_len > self._msg_len: - raise ValueError("Message is too long") - - if self._mac_status == MacStatus.PROCESSING_AUTH_DATA: - # Associated data is concatenated with the least number - # of zero bytes (possibly none) to reach alignment to - # the 16 byte boundary (A.2.3) - self._pad_cache_and_update() - self._mac_status = MacStatus.PROCESSING_PLAINTEXT - - self._update(plaintext) - return self._cipher.encrypt(plaintext, output=output) - - def decrypt(self, ciphertext, output=None): - """Decrypt data with the key set at initialization. - - A cipher object is stateful: once you have decrypted a message - you cannot decrypt (or encrypt) another message with the same - object. - - This method can be called only **once** if ``msg_len`` was - not passed at initialization. - - If ``msg_len`` was given, the data to decrypt can be - broken up in two or more pieces and `decrypt` can be - called multiple times. - - That is, the statement: - - >>> c.decrypt(a) + c.decrypt(b) - - is equivalent to: - - >>> c.decrypt(a+b) - - This function does not remove any padding from the plaintext. - - :Parameters: - ciphertext : bytes/bytearray/memoryview - The piece of data to decrypt. - It can be of any length. - :Keywords: - output : bytearray/memoryview - The location where the plaintext must be written to. - If ``None``, the plaintext is returned. - :Return: - If ``output`` is ``None``, the plaintext as ``bytes``. - Otherwise, ``None``. - """ - - if "decrypt" not in self._next: - raise TypeError("decrypt() can only be called" - " after initialization or an update()") - self._next = ["decrypt", "verify"] - - # No more associated data allowed from now - if self._assoc_len is None: - assert(isinstance(self._cache, list)) - self._assoc_len = sum([len(x) for x in self._cache]) - if self._msg_len is not None: - self._start_mac() - else: - if self._cumul_assoc_len < self._assoc_len: - raise ValueError("Associated data is too short") - - # Only once piece of ciphertext accepted if message length was - # not declared in advance - if self._msg_len is None: - self._msg_len = len(ciphertext) - self._start_mac() - self._next = ["verify"] - - self._cumul_msg_len += len(ciphertext) - if self._cumul_msg_len > self._msg_len: - raise ValueError("Message is too long") - - if self._mac_status == MacStatus.PROCESSING_AUTH_DATA: - # Associated data is concatenated with the least number - # of zero bytes (possibly none) to reach alignment to - # the 16 byte boundary (A.2.3) - self._pad_cache_and_update() - self._mac_status = MacStatus.PROCESSING_PLAINTEXT - - # Encrypt is equivalent to decrypt with the CTR mode - plaintext = self._cipher.encrypt(ciphertext, output=output) - if output is None: - self._update(plaintext) - else: - self._update(output) - return plaintext - - def digest(self): - """Compute the *binary* MAC tag. - - The caller invokes this function at the very end. - - This method returns the MAC that shall be sent to the receiver, - together with the ciphertext. - - :Return: the MAC, as a byte string. - """ - - if "digest" not in self._next: - raise TypeError("digest() cannot be called when decrypting" - " or validating a message") - self._next = ["digest"] - return self._digest() - - def _digest(self): - if self._mac_tag: - return self._mac_tag - - if self._assoc_len is None: - assert(isinstance(self._cache, list)) - self._assoc_len = sum([len(x) for x in self._cache]) - if self._msg_len is not None: - self._start_mac() - else: - if self._cumul_assoc_len < self._assoc_len: - raise ValueError("Associated data is too short") - - if self._msg_len is None: - self._msg_len = 0 - self._start_mac() - - if self._cumul_msg_len != self._msg_len: - raise ValueError("Message is too short") - - # Both associated data and payload are concatenated with the least - # number of zero bytes (possibly none) that align it to the - # 16 byte boundary (A.2.2 and A.2.3) - self._pad_cache_and_update() - - # Step 8 in 6.1 (T xor MSB_Tlen(S_0)) - self._mac_tag = strxor(self._t, self._s_0)[:self._mac_len] - - return self._mac_tag - - def hexdigest(self): - """Compute the *printable* MAC tag. - - This method is like `digest`. - - :Return: the MAC, as a hexadecimal string. - """ - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def verify(self, received_mac_tag): - """Validate the *binary* MAC tag. - - The caller invokes this function at the very end. - - This method checks if the decrypted message is indeed valid - (that is, if the key is correct) and it has not been - tampered with while in transit. - - :Parameters: - received_mac_tag : bytes/bytearray/memoryview - This is the *binary* MAC, as received from the sender. - :Raises ValueError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - if "verify" not in self._next: - raise TypeError("verify() cannot be called" - " when encrypting a message") - self._next = ["verify"] - - self._digest() - secret = get_random_bytes(16) - - mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=self._mac_tag) - mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=received_mac_tag) - - if mac1.digest() != mac2.digest(): - raise ValueError("MAC check failed") - - def hexverify(self, hex_mac_tag): - """Validate the *printable* MAC tag. - - This method is like `verify`. - - :Parameters: - hex_mac_tag : string - This is the *printable* MAC, as received from the sender. - :Raises ValueError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - self.verify(unhexlify(hex_mac_tag)) - - def encrypt_and_digest(self, plaintext, output=None): - """Perform encrypt() and digest() in one step. - - :Parameters: - plaintext : bytes/bytearray/memoryview - The piece of data to encrypt. - :Keywords: - output : bytearray/memoryview - The location where the ciphertext must be written to. - If ``None``, the ciphertext is returned. - :Return: - a tuple with two items: - - - the ciphertext, as ``bytes`` - - the MAC tag, as ``bytes`` - - The first item becomes ``None`` when the ``output`` parameter - specified a location for the result. - """ - - return self.encrypt(plaintext, output=output), self.digest() - - def decrypt_and_verify(self, ciphertext, received_mac_tag, output=None): - """Perform decrypt() and verify() in one step. - - :Parameters: - ciphertext : bytes/bytearray/memoryview - The piece of data to decrypt. - received_mac_tag : bytes/bytearray/memoryview - This is the *binary* MAC, as received from the sender. - :Keywords: - output : bytearray/memoryview - The location where the plaintext must be written to. - If ``None``, the plaintext is returned. - :Return: the plaintext as ``bytes`` or ``None`` when the ``output`` - parameter specified a location for the result. - :Raises ValueError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - plaintext = self.decrypt(ciphertext, output=output) - self.verify(received_mac_tag) - return plaintext - - -def _create_ccm_cipher(factory, **kwargs): - """Create a new block cipher, configured in CCM mode. - - :Parameters: - factory : module - A symmetric cipher module from `Cryptodome.Cipher` (like - `Cryptodome.Cipher.AES`). - - :Keywords: - key : bytes/bytearray/memoryview - The secret key to use in the symmetric cipher. - - nonce : bytes/bytearray/memoryview - A value that must never be reused for any other encryption. - - Its length must be in the range ``[7..13]``. - 11 or 12 bytes are reasonable values in general. Bear in - mind that with CCM there is a trade-off between nonce length and - maximum message size. - - If not specified, a 11 byte long random string is used. - - mac_len : integer - Length of the MAC, in bytes. It must be even and in - the range ``[4..16]``. The default is 16. - - msg_len : integer - Length of the message to (de)cipher. - If not specified, ``encrypt`` or ``decrypt`` may only be called once. - - assoc_len : integer - Length of the associated data. - If not specified, all data is internally buffered. - """ - - try: - key = key = kwargs.pop("key") - except KeyError as e: - raise TypeError("Missing parameter: " + str(e)) - - nonce = kwargs.pop("nonce", None) # N - if nonce is None: - nonce = get_random_bytes(11) - mac_len = kwargs.pop("mac_len", factory.block_size) - msg_len = kwargs.pop("msg_len", None) # p - assoc_len = kwargs.pop("assoc_len", None) # a - cipher_params = dict(kwargs) - - return CcmMode(factory, key, nonce, mac_len, msg_len, - assoc_len, cipher_params) diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_ccm.pyi b/resources/lib/deps/Cryptodome/Cipher/_mode_ccm.pyi deleted file mode 100644 index 4b9f620..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_ccm.pyi +++ /dev/null @@ -1,47 +0,0 @@ -from types import ModuleType -from typing import Union, overload, Dict, Tuple, Optional - -Buffer = Union[bytes, bytearray, memoryview] - -__all__ = ['CcmMode'] - -class CcmMode(object): - block_size: int - nonce: bytes - - def __init__(self, - factory: ModuleType, - key: Buffer, - nonce: Buffer, - mac_len: int, - msg_len: int, - assoc_len: int, - cipher_params: Dict) -> None: ... - - def update(self, assoc_data: Buffer) -> CcmMode: ... - - @overload - def encrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def encrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - @overload - def decrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def decrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def verify(self, received_mac_tag: Buffer) -> None: ... - def hexverify(self, hex_mac_tag: str) -> None: ... - - @overload - def encrypt_and_digest(self, - plaintext: Buffer) -> Tuple[bytes, bytes]: ... - @overload - def encrypt_and_digest(self, - plaintext: Buffer, - output: Buffer) -> Tuple[None, bytes]: ... - def decrypt_and_verify(self, - ciphertext: Buffer, - received_mac_tag: Buffer, - output: Optional[Union[bytearray, memoryview]] = ...) -> bytes: ... diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_cfb.py b/resources/lib/deps/Cryptodome/Cipher/_mode_cfb.py deleted file mode 100644 index 1b1b6c3..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_cfb.py +++ /dev/null @@ -1,293 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Cipher/mode_cfb.py : CFB mode -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -""" -Counter Feedback (CFB) mode. -""" - -__all__ = ['CfbMode'] - -from Cryptodome.Util.py3compat import _copy_bytes -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, - create_string_buffer, get_raw_buffer, - SmartPointer, c_size_t, c_uint8_ptr, - is_writeable_buffer) - -from Cryptodome.Random import get_random_bytes - -raw_cfb_lib = load_pycryptodome_raw_lib("Cryptodome.Cipher._raw_cfb",""" - int CFB_start_operation(void *cipher, - const uint8_t iv[], - size_t iv_len, - size_t segment_len, /* In bytes */ - void **pResult); - int CFB_encrypt(void *cfbState, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int CFB_decrypt(void *cfbState, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int CFB_stop_operation(void *state);""" - ) - - -class CfbMode(object): - """*Cipher FeedBack (CFB)*. - - This mode is similar to CFB, but it transforms - the underlying block cipher into a stream cipher. - - Plaintext and ciphertext are processed in *segments* - of **s** bits. The mode is therefore sometimes - labelled **s**-bit CFB. - - An Initialization Vector (*IV*) is required. - - See `NIST SP800-38A`_ , Section 6.3. - - .. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf - - :undocumented: __init__ - """ - - def __init__(self, block_cipher, iv, segment_size): - """Create a new block cipher, configured in CFB mode. - - :Parameters: - block_cipher : C pointer - A smart pointer to the low-level block cipher instance. - - iv : bytes/bytearray/memoryview - The initialization vector to use for encryption or decryption. - It is as long as the cipher block. - - **The IV must be unpredictable**. Ideally it is picked randomly. - - Reusing the *IV* for encryptions performed with the same key - compromises confidentiality. - - segment_size : integer - The number of bytes the plaintext and ciphertext are segmented in. - """ - - self._state = VoidPointer() - result = raw_cfb_lib.CFB_start_operation(block_cipher.get(), - c_uint8_ptr(iv), - c_size_t(len(iv)), - c_size_t(segment_size), - self._state.address_of()) - if result: - raise ValueError("Error %d while instantiating the CFB mode" % result) - - # Ensure that object disposal of this Python object will (eventually) - # free the memory allocated by the raw library for the cipher mode - self._state = SmartPointer(self._state.get(), - raw_cfb_lib.CFB_stop_operation) - - # Memory allocated for the underlying block cipher is now owed - # by the cipher mode - block_cipher.release() - - self.block_size = len(iv) - """The block size of the underlying cipher, in bytes.""" - - self.iv = _copy_bytes(None, None, iv) - """The Initialization Vector originally used to create the object. - The value does not change.""" - - self.IV = self.iv - """Alias for `iv`""" - - self._next = ["encrypt", "decrypt"] - - def encrypt(self, plaintext, output=None): - """Encrypt data with the key and the parameters set at initialization. - - A cipher object is stateful: once you have encrypted a message - you cannot encrypt (or decrypt) another message using the same - object. - - The data to encrypt can be broken up in two or - more pieces and `encrypt` can be called multiple times. - - That is, the statement: - - >>> c.encrypt(a) + c.encrypt(b) - - is equivalent to: - - >>> c.encrypt(a+b) - - This function does not add any padding to the plaintext. - - :Parameters: - plaintext : bytes/bytearray/memoryview - The piece of data to encrypt. - It can be of any length. - :Keywords: - output : bytearray/memoryview - The location where the ciphertext must be written to. - If ``None``, the ciphertext is returned. - :Return: - If ``output`` is ``None``, the ciphertext is returned as ``bytes``. - Otherwise, ``None``. - """ - - if "encrypt" not in self._next: - raise TypeError("encrypt() cannot be called after decrypt()") - self._next = ["encrypt"] - - if output is None: - ciphertext = create_string_buffer(len(plaintext)) - else: - ciphertext = output - - if not is_writeable_buffer(output): - raise TypeError("output must be a bytearray or a writeable memoryview") - - if len(plaintext) != len(output): - raise ValueError("output must have the same length as the input" - " (%d bytes)" % len(plaintext)) - - result = raw_cfb_lib.CFB_encrypt(self._state.get(), - c_uint8_ptr(plaintext), - c_uint8_ptr(ciphertext), - c_size_t(len(plaintext))) - if result: - raise ValueError("Error %d while encrypting in CFB mode" % result) - - if output is None: - return get_raw_buffer(ciphertext) - else: - return None - - def decrypt(self, ciphertext, output=None): - """Decrypt data with the key and the parameters set at initialization. - - A cipher object is stateful: once you have decrypted a message - you cannot decrypt (or encrypt) another message with the same - object. - - The data to decrypt can be broken up in two or - more pieces and `decrypt` can be called multiple times. - - That is, the statement: - - >>> c.decrypt(a) + c.decrypt(b) - - is equivalent to: - - >>> c.decrypt(a+b) - - This function does not remove any padding from the plaintext. - - :Parameters: - ciphertext : bytes/bytearray/memoryview - The piece of data to decrypt. - It can be of any length. - :Keywords: - output : bytearray/memoryview - The location where the plaintext must be written to. - If ``None``, the plaintext is returned. - :Return: - If ``output`` is ``None``, the plaintext is returned as ``bytes``. - Otherwise, ``None``. - """ - - if "decrypt" not in self._next: - raise TypeError("decrypt() cannot be called after encrypt()") - self._next = ["decrypt"] - - if output is None: - plaintext = create_string_buffer(len(ciphertext)) - else: - plaintext = output - - if not is_writeable_buffer(output): - raise TypeError("output must be a bytearray or a writeable memoryview") - - if len(ciphertext) != len(output): - raise ValueError("output must have the same length as the input" - " (%d bytes)" % len(plaintext)) - - result = raw_cfb_lib.CFB_decrypt(self._state.get(), - c_uint8_ptr(ciphertext), - c_uint8_ptr(plaintext), - c_size_t(len(ciphertext))) - if result: - raise ValueError("Error %d while decrypting in CFB mode" % result) - - if output is None: - return get_raw_buffer(plaintext) - else: - return None - - -def _create_cfb_cipher(factory, **kwargs): - """Instantiate a cipher object that performs CFB encryption/decryption. - - :Parameters: - factory : module - The underlying block cipher, a module from ``Cryptodome.Cipher``. - - :Keywords: - iv : bytes/bytearray/memoryview - The IV to use for CFB. - - IV : bytes/bytearray/memoryview - Alias for ``iv``. - - segment_size : integer - The number of bit the plaintext and ciphertext are segmented in. - If not present, the default is 8. - - Any other keyword will be passed to the underlying block cipher. - See the relevant documentation for details (at least ``key`` will need - to be present). - """ - - cipher_state = factory._create_base_cipher(kwargs) - - iv = kwargs.pop("IV", None) - IV = kwargs.pop("iv", None) - - if (None, None) == (iv, IV): - iv = get_random_bytes(factory.block_size) - if iv is not None: - if IV is not None: - raise TypeError("You must either use 'iv' or 'IV', not both") - else: - iv = IV - - if len(iv) != factory.block_size: - raise ValueError("Incorrect IV length (it must be %d bytes long)" % - factory.block_size) - - segment_size_bytes, rem = divmod(kwargs.pop("segment_size", 8), 8) - if segment_size_bytes == 0 or rem != 0: - raise ValueError("'segment_size' must be positive and multiple of 8 bits") - - if kwargs: - raise TypeError("Unknown parameters for CFB: %s" % str(kwargs)) - return CfbMode(cipher_state, iv, segment_size_bytes) diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_cfb.pyi b/resources/lib/deps/Cryptodome/Cipher/_mode_cfb.pyi deleted file mode 100644 index 228e464..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_cfb.pyi +++ /dev/null @@ -1,26 +0,0 @@ -from typing import Union, overload - -from Cryptodome.Util._raw_api import SmartPointer - -Buffer = Union[bytes, bytearray, memoryview] - -__all__ = ['CfbMode'] - - -class CfbMode(object): - block_size: int - iv: Buffer - IV: Buffer - - def __init__(self, - block_cipher: SmartPointer, - iv: Buffer, - segment_size: int) -> None: ... - @overload - def encrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def encrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - @overload - def decrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def decrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_ctr.py b/resources/lib/deps/Cryptodome/Cipher/_mode_ctr.py deleted file mode 100644 index 9ce357f..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_ctr.py +++ /dev/null @@ -1,393 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Cipher/mode_ctr.py : CTR mode -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -""" -Counter (CTR) mode. -""" - -__all__ = ['CtrMode'] - -import struct - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, - create_string_buffer, get_raw_buffer, - SmartPointer, c_size_t, c_uint8_ptr, - is_writeable_buffer) - -from Cryptodome.Random import get_random_bytes -from Cryptodome.Util.py3compat import _copy_bytes, is_native_int -from Cryptodome.Util.number import long_to_bytes - -raw_ctr_lib = load_pycryptodome_raw_lib("Cryptodome.Cipher._raw_ctr", """ - int CTR_start_operation(void *cipher, - uint8_t initialCounterBlock[], - size_t initialCounterBlock_len, - size_t prefix_len, - unsigned counter_len, - unsigned littleEndian, - void **pResult); - int CTR_encrypt(void *ctrState, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int CTR_decrypt(void *ctrState, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int CTR_stop_operation(void *ctrState);""" - ) - - -class CtrMode(object): - """*CounTeR (CTR)* mode. - - This mode is very similar to ECB, in that - encryption of one block is done independently of all other blocks. - - Unlike ECB, the block *position* contributes to the encryption - and no information leaks about symbol frequency. - - Each message block is associated to a *counter* which - must be unique across all messages that get encrypted - with the same key (not just within the same message). - The counter is as big as the block size. - - Counters can be generated in several ways. The most - straightword one is to choose an *initial counter block* - (which can be made public, similarly to the *IV* for the - other modes) and increment its lowest **m** bits by one - (modulo *2^m*) for each block. In most cases, **m** is - chosen to be half the block size. - - See `NIST SP800-38A`_, Section 6.5 (for the mode) and - Appendix B (for how to manage the *initial counter block*). - - .. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf - - :undocumented: __init__ - """ - - def __init__(self, block_cipher, initial_counter_block, - prefix_len, counter_len, little_endian): - """Create a new block cipher, configured in CTR mode. - - :Parameters: - block_cipher : C pointer - A smart pointer to the low-level block cipher instance. - - initial_counter_block : bytes/bytearray/memoryview - The initial plaintext to use to generate the key stream. - - It is as large as the cipher block, and it embeds - the initial value of the counter. - - This value must not be reused. - It shall contain a nonce or a random component. - Reusing the *initial counter block* for encryptions - performed with the same key compromises confidentiality. - - prefix_len : integer - The amount of bytes at the beginning of the counter block - that never change. - - counter_len : integer - The length in bytes of the counter embedded in the counter - block. - - little_endian : boolean - True if the counter in the counter block is an integer encoded - in little endian mode. If False, it is big endian. - """ - - if len(initial_counter_block) == prefix_len + counter_len: - self.nonce = _copy_bytes(None, prefix_len, initial_counter_block) - """Nonce; not available if there is a fixed suffix""" - - self._state = VoidPointer() - result = raw_ctr_lib.CTR_start_operation(block_cipher.get(), - c_uint8_ptr(initial_counter_block), - c_size_t(len(initial_counter_block)), - c_size_t(prefix_len), - counter_len, - little_endian, - self._state.address_of()) - if result: - raise ValueError("Error %X while instantiating the CTR mode" - % result) - - # Ensure that object disposal of this Python object will (eventually) - # free the memory allocated by the raw library for the cipher mode - self._state = SmartPointer(self._state.get(), - raw_ctr_lib.CTR_stop_operation) - - # Memory allocated for the underlying block cipher is now owed - # by the cipher mode - block_cipher.release() - - self.block_size = len(initial_counter_block) - """The block size of the underlying cipher, in bytes.""" - - self._next = ["encrypt", "decrypt"] - - def encrypt(self, plaintext, output=None): - """Encrypt data with the key and the parameters set at initialization. - - A cipher object is stateful: once you have encrypted a message - you cannot encrypt (or decrypt) another message using the same - object. - - The data to encrypt can be broken up in two or - more pieces and `encrypt` can be called multiple times. - - That is, the statement: - - >>> c.encrypt(a) + c.encrypt(b) - - is equivalent to: - - >>> c.encrypt(a+b) - - This function does not add any padding to the plaintext. - - :Parameters: - plaintext : bytes/bytearray/memoryview - The piece of data to encrypt. - It can be of any length. - :Keywords: - output : bytearray/memoryview - The location where the ciphertext must be written to. - If ``None``, the ciphertext is returned. - :Return: - If ``output`` is ``None``, the ciphertext is returned as ``bytes``. - Otherwise, ``None``. - """ - - if "encrypt" not in self._next: - raise TypeError("encrypt() cannot be called after decrypt()") - self._next = ["encrypt"] - - if output is None: - ciphertext = create_string_buffer(len(plaintext)) - else: - ciphertext = output - - if not is_writeable_buffer(output): - raise TypeError("output must be a bytearray or a writeable memoryview") - - if len(plaintext) != len(output): - raise ValueError("output must have the same length as the input" - " (%d bytes)" % len(plaintext)) - - result = raw_ctr_lib.CTR_encrypt(self._state.get(), - c_uint8_ptr(plaintext), - c_uint8_ptr(ciphertext), - c_size_t(len(plaintext))) - if result: - if result == 0x60002: - raise OverflowError("The counter has wrapped around in" - " CTR mode") - raise ValueError("Error %X while encrypting in CTR mode" % result) - - if output is None: - return get_raw_buffer(ciphertext) - else: - return None - - def decrypt(self, ciphertext, output=None): - """Decrypt data with the key and the parameters set at initialization. - - A cipher object is stateful: once you have decrypted a message - you cannot decrypt (or encrypt) another message with the same - object. - - The data to decrypt can be broken up in two or - more pieces and `decrypt` can be called multiple times. - - That is, the statement: - - >>> c.decrypt(a) + c.decrypt(b) - - is equivalent to: - - >>> c.decrypt(a+b) - - This function does not remove any padding from the plaintext. - - :Parameters: - ciphertext : bytes/bytearray/memoryview - The piece of data to decrypt. - It can be of any length. - :Keywords: - output : bytearray/memoryview - The location where the plaintext must be written to. - If ``None``, the plaintext is returned. - :Return: - If ``output`` is ``None``, the plaintext is returned as ``bytes``. - Otherwise, ``None``. - """ - - if "decrypt" not in self._next: - raise TypeError("decrypt() cannot be called after encrypt()") - self._next = ["decrypt"] - - if output is None: - plaintext = create_string_buffer(len(ciphertext)) - else: - plaintext = output - - if not is_writeable_buffer(output): - raise TypeError("output must be a bytearray or a writeable memoryview") - - if len(ciphertext) != len(output): - raise ValueError("output must have the same length as the input" - " (%d bytes)" % len(plaintext)) - - result = raw_ctr_lib.CTR_decrypt(self._state.get(), - c_uint8_ptr(ciphertext), - c_uint8_ptr(plaintext), - c_size_t(len(ciphertext))) - if result: - if result == 0x60002: - raise OverflowError("The counter has wrapped around in" - " CTR mode") - raise ValueError("Error %X while decrypting in CTR mode" % result) - - if output is None: - return get_raw_buffer(plaintext) - else: - return None - - -def _create_ctr_cipher(factory, **kwargs): - """Instantiate a cipher object that performs CTR encryption/decryption. - - :Parameters: - factory : module - The underlying block cipher, a module from ``Cryptodome.Cipher``. - - :Keywords: - nonce : bytes/bytearray/memoryview - The fixed part at the beginning of the counter block - the rest is - the counter number that gets increased when processing the next block. - The nonce must be such that no two messages are encrypted under the - same key and the same nonce. - - The nonce must be shorter than the block size (it can have - zero length; the counter is then as long as the block). - - If this parameter is not present, a random nonce will be created with - length equal to half the block size. No random nonce shorter than - 64 bits will be created though - you must really think through all - security consequences of using such a short block size. - - initial_value : posive integer or bytes/bytearray/memoryview - The initial value for the counter. If not present, the cipher will - start counting from 0. The value is incremented by one for each block. - The counter number is encoded in big endian mode. - - counter : object - Instance of ``Cryptodome.Util.Counter``, which allows full customization - of the counter block. This parameter is incompatible to both ``nonce`` - and ``initial_value``. - - Any other keyword will be passed to the underlying block cipher. - See the relevant documentation for details (at least ``key`` will need - to be present). - """ - - cipher_state = factory._create_base_cipher(kwargs) - - counter = kwargs.pop("counter", None) - nonce = kwargs.pop("nonce", None) - initial_value = kwargs.pop("initial_value", None) - if kwargs: - raise TypeError("Invalid parameters for CTR mode: %s" % str(kwargs)) - - if counter is not None and (nonce, initial_value) != (None, None): - raise TypeError("'counter' and 'nonce'/'initial_value'" - " are mutually exclusive") - - if counter is None: - # Cryptodome.Util.Counter is not used - if nonce is None: - if factory.block_size < 16: - raise TypeError("Impossible to create a safe nonce for short" - " block sizes") - nonce = get_random_bytes(factory.block_size // 2) - else: - if len(nonce) >= factory.block_size: - raise ValueError("Nonce is too long") - - # What is not nonce is counter - counter_len = factory.block_size - len(nonce) - - if initial_value is None: - initial_value = 0 - - if is_native_int(initial_value): - if (1 << (counter_len * 8)) - 1 < initial_value: - raise ValueError("Initial counter value is too large") - initial_counter_block = nonce + long_to_bytes(initial_value, counter_len) - else: - if len(initial_value) != counter_len: - raise ValueError("Incorrect length for counter byte string (%d bytes, expected %d)" % - (len(initial_value), counter_len)) - initial_counter_block = nonce + initial_value - - return CtrMode(cipher_state, - initial_counter_block, - len(nonce), # prefix - counter_len, - False) # little_endian - - # Cryptodome.Util.Counter is used - - # 'counter' used to be a callable object, but now it is - # just a dictionary for backward compatibility. - _counter = dict(counter) - try: - counter_len = _counter.pop("counter_len") - prefix = _counter.pop("prefix") - suffix = _counter.pop("suffix") - initial_value = _counter.pop("initial_value") - little_endian = _counter.pop("little_endian") - except KeyError: - raise TypeError("Incorrect counter object" - " (use Cryptodome.Util.Counter.new)") - - # Compute initial counter block - words = [] - while initial_value > 0: - words.append(struct.pack('B', initial_value & 255)) - initial_value >>= 8 - words += [b'\x00'] * max(0, counter_len - len(words)) - if not little_endian: - words.reverse() - initial_counter_block = prefix + b"".join(words) + suffix - - if len(initial_counter_block) != factory.block_size: - raise ValueError("Size of the counter block (%d bytes) must match" - " block size (%d)" % (len(initial_counter_block), - factory.block_size)) - - return CtrMode(cipher_state, initial_counter_block, - len(prefix), counter_len, little_endian) diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_ctr.pyi b/resources/lib/deps/Cryptodome/Cipher/_mode_ctr.pyi deleted file mode 100644 index a68a890..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_ctr.pyi +++ /dev/null @@ -1,27 +0,0 @@ -from typing import Union, overload - -from Cryptodome.Util._raw_api import SmartPointer - -Buffer = Union[bytes, bytearray, memoryview] - -__all__ = ['CtrMode'] - -class CtrMode(object): - block_size: int - nonce: bytes - - def __init__(self, - block_cipher: SmartPointer, - initial_counter_block: Buffer, - prefix_len: int, - counter_len: int, - little_endian: bool) -> None: ... - @overload - def encrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def encrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - @overload - def decrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def decrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_eax.py b/resources/lib/deps/Cryptodome/Cipher/_mode_eax.py deleted file mode 100644 index 44ef21f..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_eax.py +++ /dev/null @@ -1,408 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -""" -EAX mode. -""" - -__all__ = ['EaxMode'] - -import struct -from binascii import unhexlify - -from Cryptodome.Util.py3compat import byte_string, bord, _copy_bytes - -from Cryptodome.Util._raw_api import is_buffer - -from Cryptodome.Util.strxor import strxor -from Cryptodome.Util.number import long_to_bytes, bytes_to_long - -from Cryptodome.Hash import CMAC, BLAKE2s -from Cryptodome.Random import get_random_bytes - - -class EaxMode(object): - """*EAX* mode. - - This is an Authenticated Encryption with Associated Data - (`AEAD`_) mode. It provides both confidentiality and authenticity. - - The header of the message may be left in the clear, if needed, - and it will still be subject to authentication. - - The decryption step tells the receiver if the message comes - from a source that really knowns the secret key. - Additionally, decryption detects if any part of the message - - including the header - has been modified or corrupted. - - This mode requires a *nonce*. - - This mode is only available for ciphers that operate on 64 or - 128 bits blocks. - - There are no official standards defining EAX. - The implementation is based on `a proposal`__ that - was presented to NIST. - - .. _AEAD: http://blog.cryptographyengineering.com/2012/05/how-to-choose-authenticated-encryption.html - .. __: http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/eax/eax-spec.pdf - - :undocumented: __init__ - """ - - def __init__(self, factory, key, nonce, mac_len, cipher_params): - """EAX cipher mode""" - - self.block_size = factory.block_size - """The block size of the underlying cipher, in bytes.""" - - self.nonce = _copy_bytes(None, None, nonce) - """The nonce originally used to create the object.""" - - self._mac_len = mac_len - self._mac_tag = None # Cache for MAC tag - - # Allowed transitions after initialization - self._next = ["update", "encrypt", "decrypt", - "digest", "verify"] - - # MAC tag length - if not (2 <= self._mac_len <= self.block_size): - raise ValueError("'mac_len' must be at least 2 and not larger than %d" - % self.block_size) - - # Nonce cannot be empty and must be a byte string - if len(self.nonce) == 0: - raise ValueError("Nonce cannot be empty in EAX mode") - if not is_buffer(nonce): - raise TypeError("nonce must be bytes, bytearray or memoryview") - - self._omac = [ - CMAC.new(key, - b'\x00' * (self.block_size - 1) + struct.pack('B', i), - ciphermod=factory, - cipher_params=cipher_params) - for i in range(0, 3) - ] - - # Compute MAC of nonce - self._omac[0].update(self.nonce) - self._signer = self._omac[1] - - # MAC of the nonce is also the initial counter for CTR encryption - counter_int = bytes_to_long(self._omac[0].digest()) - self._cipher = factory.new(key, - factory.MODE_CTR, - initial_value=counter_int, - nonce=b"", - **cipher_params) - - def update(self, assoc_data): - """Protect associated data - - If there is any associated data, the caller has to invoke - this function one or more times, before using - ``decrypt`` or ``encrypt``. - - By *associated data* it is meant any data (e.g. packet headers) that - will not be encrypted and will be transmitted in the clear. - However, the receiver is still able to detect any modification to it. - - If there is no associated data, this method must not be called. - - The caller may split associated data in segments of any size, and - invoke this method multiple times, each time with the next segment. - - :Parameters: - assoc_data : bytes/bytearray/memoryview - A piece of associated data. There are no restrictions on its size. - """ - - if "update" not in self._next: - raise TypeError("update() can only be called" - " immediately after initialization") - - self._next = ["update", "encrypt", "decrypt", - "digest", "verify"] - - self._signer.update(assoc_data) - return self - - def encrypt(self, plaintext, output=None): - """Encrypt data with the key and the parameters set at initialization. - - A cipher object is stateful: once you have encrypted a message - you cannot encrypt (or decrypt) another message using the same - object. - - The data to encrypt can be broken up in two or - more pieces and `encrypt` can be called multiple times. - - That is, the statement: - - >>> c.encrypt(a) + c.encrypt(b) - - is equivalent to: - - >>> c.encrypt(a+b) - - This function does not add any padding to the plaintext. - - :Parameters: - plaintext : bytes/bytearray/memoryview - The piece of data to encrypt. - It can be of any length. - :Keywords: - output : bytearray/memoryview - The location where the ciphertext must be written to. - If ``None``, the ciphertext is returned. - :Return: - If ``output`` is ``None``, the ciphertext as ``bytes``. - Otherwise, ``None``. - """ - - if "encrypt" not in self._next: - raise TypeError("encrypt() can only be called after" - " initialization or an update()") - self._next = ["encrypt", "digest"] - ct = self._cipher.encrypt(plaintext, output=output) - if output is None: - self._omac[2].update(ct) - else: - self._omac[2].update(output) - return ct - - def decrypt(self, ciphertext, output=None): - """Decrypt data with the key and the parameters set at initialization. - - A cipher object is stateful: once you have decrypted a message - you cannot decrypt (or encrypt) another message with the same - object. - - The data to decrypt can be broken up in two or - more pieces and `decrypt` can be called multiple times. - - That is, the statement: - - >>> c.decrypt(a) + c.decrypt(b) - - is equivalent to: - - >>> c.decrypt(a+b) - - This function does not remove any padding from the plaintext. - - :Parameters: - ciphertext : bytes/bytearray/memoryview - The piece of data to decrypt. - It can be of any length. - :Keywords: - output : bytearray/memoryview - The location where the plaintext must be written to. - If ``None``, the plaintext is returned. - :Return: - If ``output`` is ``None``, the plaintext as ``bytes``. - Otherwise, ``None``. - """ - - if "decrypt" not in self._next: - raise TypeError("decrypt() can only be called" - " after initialization or an update()") - self._next = ["decrypt", "verify"] - self._omac[2].update(ciphertext) - return self._cipher.decrypt(ciphertext, output=output) - - def digest(self): - """Compute the *binary* MAC tag. - - The caller invokes this function at the very end. - - This method returns the MAC that shall be sent to the receiver, - together with the ciphertext. - - :Return: the MAC, as a byte string. - """ - - if "digest" not in self._next: - raise TypeError("digest() cannot be called when decrypting" - " or validating a message") - self._next = ["digest"] - - if not self._mac_tag: - tag = b'\x00' * self.block_size - for i in range(3): - tag = strxor(tag, self._omac[i].digest()) - self._mac_tag = tag[:self._mac_len] - - return self._mac_tag - - def hexdigest(self): - """Compute the *printable* MAC tag. - - This method is like `digest`. - - :Return: the MAC, as a hexadecimal string. - """ - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def verify(self, received_mac_tag): - """Validate the *binary* MAC tag. - - The caller invokes this function at the very end. - - This method checks if the decrypted message is indeed valid - (that is, if the key is correct) and it has not been - tampered with while in transit. - - :Parameters: - received_mac_tag : bytes/bytearray/memoryview - This is the *binary* MAC, as received from the sender. - :Raises MacMismatchError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - if "verify" not in self._next: - raise TypeError("verify() cannot be called" - " when encrypting a message") - self._next = ["verify"] - - if not self._mac_tag: - tag = b'\x00' * self.block_size - for i in range(3): - tag = strxor(tag, self._omac[i].digest()) - self._mac_tag = tag[:self._mac_len] - - secret = get_random_bytes(16) - - mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=self._mac_tag) - mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=received_mac_tag) - - if mac1.digest() != mac2.digest(): - raise ValueError("MAC check failed") - - def hexverify(self, hex_mac_tag): - """Validate the *printable* MAC tag. - - This method is like `verify`. - - :Parameters: - hex_mac_tag : string - This is the *printable* MAC, as received from the sender. - :Raises MacMismatchError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - self.verify(unhexlify(hex_mac_tag)) - - def encrypt_and_digest(self, plaintext, output=None): - """Perform encrypt() and digest() in one step. - - :Parameters: - plaintext : bytes/bytearray/memoryview - The piece of data to encrypt. - :Keywords: - output : bytearray/memoryview - The location where the ciphertext must be written to. - If ``None``, the ciphertext is returned. - :Return: - a tuple with two items: - - - the ciphertext, as ``bytes`` - - the MAC tag, as ``bytes`` - - The first item becomes ``None`` when the ``output`` parameter - specified a location for the result. - """ - - return self.encrypt(plaintext, output=output), self.digest() - - def decrypt_and_verify(self, ciphertext, received_mac_tag, output=None): - """Perform decrypt() and verify() in one step. - - :Parameters: - ciphertext : bytes/bytearray/memoryview - The piece of data to decrypt. - received_mac_tag : bytes/bytearray/memoryview - This is the *binary* MAC, as received from the sender. - :Keywords: - output : bytearray/memoryview - The location where the plaintext must be written to. - If ``None``, the plaintext is returned. - :Return: the plaintext as ``bytes`` or ``None`` when the ``output`` - parameter specified a location for the result. - :Raises MacMismatchError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - pt = self.decrypt(ciphertext, output=output) - self.verify(received_mac_tag) - return pt - - -def _create_eax_cipher(factory, **kwargs): - """Create a new block cipher, configured in EAX mode. - - :Parameters: - factory : module - A symmetric cipher module from `Cryptodome.Cipher` (like - `Cryptodome.Cipher.AES`). - - :Keywords: - key : bytes/bytearray/memoryview - The secret key to use in the symmetric cipher. - - nonce : bytes/bytearray/memoryview - A value that must never be reused for any other encryption. - There are no restrictions on its length, but it is recommended to use - at least 16 bytes. - - The nonce shall never repeat for two different messages encrypted with - the same key, but it does not need to be random. - - If not specified, a 16 byte long random string is used. - - mac_len : integer - Length of the MAC, in bytes. It must be no larger than the cipher - block bytes (which is the default). - """ - - try: - key = kwargs.pop("key") - nonce = kwargs.pop("nonce", None) - if nonce is None: - nonce = get_random_bytes(16) - mac_len = kwargs.pop("mac_len", factory.block_size) - except KeyError as e: - raise TypeError("Missing parameter: " + str(e)) - - return EaxMode(factory, key, nonce, mac_len, kwargs) diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_eax.pyi b/resources/lib/deps/Cryptodome/Cipher/_mode_eax.pyi deleted file mode 100644 index cbfa467..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_eax.pyi +++ /dev/null @@ -1,45 +0,0 @@ -from types import ModuleType -from typing import Any, Union, Tuple, Dict, overload, Optional - -Buffer = Union[bytes, bytearray, memoryview] - -__all__ = ['EaxMode'] - -class EaxMode(object): - block_size: int - nonce: bytes - - def __init__(self, - factory: ModuleType, - key: Buffer, - nonce: Buffer, - mac_len: int, - cipher_params: Dict) -> None: ... - - def update(self, assoc_data: Buffer) -> EaxMode: ... - - @overload - def encrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def encrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - @overload - def decrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def decrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def verify(self, received_mac_tag: Buffer) -> None: ... - def hexverify(self, hex_mac_tag: str) -> None: ... - - @overload - def encrypt_and_digest(self, - plaintext: Buffer) -> Tuple[bytes, bytes]: ... - @overload - def encrypt_and_digest(self, - plaintext: Buffer, - output: Buffer) -> Tuple[None, bytes]: ... - def decrypt_and_verify(self, - ciphertext: Buffer, - received_mac_tag: Buffer, - output: Optional[Union[bytearray, memoryview]] = ...) -> bytes: ... diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_ecb.py b/resources/lib/deps/Cryptodome/Cipher/_mode_ecb.py deleted file mode 100644 index a01a16f..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_ecb.py +++ /dev/null @@ -1,220 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Cipher/mode_ecb.py : ECB mode -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -""" -Electronic Code Book (ECB) mode. -""" - -__all__ = [ 'EcbMode' ] - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, create_string_buffer, - get_raw_buffer, SmartPointer, - c_size_t, c_uint8_ptr, - is_writeable_buffer) - -raw_ecb_lib = load_pycryptodome_raw_lib("Cryptodome.Cipher._raw_ecb", """ - int ECB_start_operation(void *cipher, - void **pResult); - int ECB_encrypt(void *ecbState, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int ECB_decrypt(void *ecbState, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int ECB_stop_operation(void *state); - """ - ) - - -class EcbMode(object): - """*Electronic Code Book (ECB)*. - - This is the simplest encryption mode. Each of the plaintext blocks - is directly encrypted into a ciphertext block, independently of - any other block. - - This mode is dangerous because it exposes frequency of symbols - in your plaintext. Other modes (e.g. *CBC*) should be used instead. - - See `NIST SP800-38A`_ , Section 6.1. - - .. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf - - :undocumented: __init__ - """ - - def __init__(self, block_cipher): - """Create a new block cipher, configured in ECB mode. - - :Parameters: - block_cipher : C pointer - A smart pointer to the low-level block cipher instance. - """ - self.block_size = block_cipher.block_size - - self._state = VoidPointer() - result = raw_ecb_lib.ECB_start_operation(block_cipher.get(), - self._state.address_of()) - if result: - raise ValueError("Error %d while instantiating the ECB mode" - % result) - - # Ensure that object disposal of this Python object will (eventually) - # free the memory allocated by the raw library for the cipher - # mode - self._state = SmartPointer(self._state.get(), - raw_ecb_lib.ECB_stop_operation) - - # Memory allocated for the underlying block cipher is now owned - # by the cipher mode - block_cipher.release() - - def encrypt(self, plaintext, output=None): - """Encrypt data with the key set at initialization. - - The data to encrypt can be broken up in two or - more pieces and `encrypt` can be called multiple times. - - That is, the statement: - - >>> c.encrypt(a) + c.encrypt(b) - - is equivalent to: - - >>> c.encrypt(a+b) - - This function does not add any padding to the plaintext. - - :Parameters: - plaintext : bytes/bytearray/memoryview - The piece of data to encrypt. - The length must be multiple of the cipher block length. - :Keywords: - output : bytearray/memoryview - The location where the ciphertext must be written to. - If ``None``, the ciphertext is returned. - :Return: - If ``output`` is ``None``, the ciphertext is returned as ``bytes``. - Otherwise, ``None``. - """ - - if output is None: - ciphertext = create_string_buffer(len(plaintext)) - else: - ciphertext = output - - if not is_writeable_buffer(output): - raise TypeError("output must be a bytearray or a writeable memoryview") - - if len(plaintext) != len(output): - raise ValueError("output must have the same length as the input" - " (%d bytes)" % len(plaintext)) - - result = raw_ecb_lib.ECB_encrypt(self._state.get(), - c_uint8_ptr(plaintext), - c_uint8_ptr(ciphertext), - c_size_t(len(plaintext))) - if result: - if result == 3: - raise ValueError("Data must be aligned to block boundary in ECB mode") - raise ValueError("Error %d while encrypting in ECB mode" % result) - - if output is None: - return get_raw_buffer(ciphertext) - else: - return None - - def decrypt(self, ciphertext, output=None): - """Decrypt data with the key set at initialization. - - The data to decrypt can be broken up in two or - more pieces and `decrypt` can be called multiple times. - - That is, the statement: - - >>> c.decrypt(a) + c.decrypt(b) - - is equivalent to: - - >>> c.decrypt(a+b) - - This function does not remove any padding from the plaintext. - - :Parameters: - ciphertext : bytes/bytearray/memoryview - The piece of data to decrypt. - The length must be multiple of the cipher block length. - :Keywords: - output : bytearray/memoryview - The location where the plaintext must be written to. - If ``None``, the plaintext is returned. - :Return: - If ``output`` is ``None``, the plaintext is returned as ``bytes``. - Otherwise, ``None``. - """ - - if output is None: - plaintext = create_string_buffer(len(ciphertext)) - else: - plaintext = output - - if not is_writeable_buffer(output): - raise TypeError("output must be a bytearray or a writeable memoryview") - - if len(ciphertext) != len(output): - raise ValueError("output must have the same length as the input" - " (%d bytes)" % len(plaintext)) - - result = raw_ecb_lib.ECB_decrypt(self._state.get(), - c_uint8_ptr(ciphertext), - c_uint8_ptr(plaintext), - c_size_t(len(ciphertext))) - if result: - if result == 3: - raise ValueError("Data must be aligned to block boundary in ECB mode") - raise ValueError("Error %d while decrypting in ECB mode" % result) - - if output is None: - return get_raw_buffer(plaintext) - else: - return None - - -def _create_ecb_cipher(factory, **kwargs): - """Instantiate a cipher object that performs ECB encryption/decryption. - - :Parameters: - factory : module - The underlying block cipher, a module from ``Cryptodome.Cipher``. - - All keywords are passed to the underlying block cipher. - See the relevant documentation for details (at least ``key`` will need - to be present""" - - cipher_state = factory._create_base_cipher(kwargs) - cipher_state.block_size = factory.block_size - if kwargs: - raise TypeError("Unknown parameters for ECB: %s" % str(kwargs)) - return EcbMode(cipher_state) diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_ecb.pyi b/resources/lib/deps/Cryptodome/Cipher/_mode_ecb.pyi deleted file mode 100644 index 936195f..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_ecb.pyi +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Union, overload - -from Cryptodome.Util._raw_api import SmartPointer - -Buffer = Union[bytes, bytearray, memoryview] - -__all__ = [ 'EcbMode' ] - -class EcbMode(object): - def __init__(self, block_cipher: SmartPointer) -> None: ... - @overload - def encrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def encrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - @overload - def decrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def decrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_gcm.py b/resources/lib/deps/Cryptodome/Cipher/_mode_gcm.py deleted file mode 100644 index 9914400..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_gcm.py +++ /dev/null @@ -1,620 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -""" -Galois/Counter Mode (GCM). -""" - -__all__ = ['GcmMode'] - -from binascii import unhexlify - -from Cryptodome.Util.py3compat import bord, _copy_bytes - -from Cryptodome.Util._raw_api import is_buffer - -from Cryptodome.Util.number import long_to_bytes, bytes_to_long -from Cryptodome.Hash import BLAKE2s -from Cryptodome.Random import get_random_bytes - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, - create_string_buffer, get_raw_buffer, - SmartPointer, c_size_t, c_uint8_ptr) - -from Cryptodome.Util import _cpu_features - - -# C API by module implementing GHASH -_ghash_api_template = """ - int ghash_%imp%(uint8_t y_out[16], - const uint8_t block_data[], - size_t len, - const uint8_t y_in[16], - const void *exp_key); - int ghash_expand_%imp%(const uint8_t h[16], - void **ghash_tables); - int ghash_destroy_%imp%(void *ghash_tables); -""" - -def _build_impl(lib, postfix): - from collections import namedtuple - - funcs = ( "ghash", "ghash_expand", "ghash_destroy" ) - GHASH_Imp = namedtuple('_GHash_Imp', funcs) - try: - imp_funcs = [ getattr(lib, x + "_" + postfix) for x in funcs ] - except AttributeError: # Make sphinx stop complaining with its mocklib - imp_funcs = [ None ] * 3 - params = dict(zip(funcs, imp_funcs)) - return GHASH_Imp(**params) - - -def _get_ghash_portable(): - api = _ghash_api_template.replace("%imp%", "portable") - lib = load_pycryptodome_raw_lib("Cryptodome.Hash._ghash_portable", api) - result = _build_impl(lib, "portable") - return result -_ghash_portable = _get_ghash_portable() - - -def _get_ghash_clmul(): - """Return None if CLMUL implementation is not available""" - - if not _cpu_features.have_clmul(): - return None - try: - api = _ghash_api_template.replace("%imp%", "clmul") - lib = load_pycryptodome_raw_lib("Cryptodome.Hash._ghash_clmul", api) - result = _build_impl(lib, "clmul") - except OSError: - result = None - return result -_ghash_clmul = _get_ghash_clmul() - - -class _GHASH(object): - """GHASH function defined in NIST SP 800-38D, Algorithm 2. - - If X_1, X_2, .. X_m are the blocks of input data, the function - computes: - - X_1*H^{m} + X_2*H^{m-1} + ... + X_m*H - - in the Galois field GF(2^256) using the reducing polynomial - (x^128 + x^7 + x^2 + x + 1). - """ - - def __init__(self, subkey, ghash_c): - assert len(subkey) == 16 - - self.ghash_c = ghash_c - - self._exp_key = VoidPointer() - result = ghash_c.ghash_expand(c_uint8_ptr(subkey), - self._exp_key.address_of()) - if result: - raise ValueError("Error %d while expanding the GHASH key" % result) - - self._exp_key = SmartPointer(self._exp_key.get(), - ghash_c.ghash_destroy) - - # create_string_buffer always returns a string of zeroes - self._last_y = create_string_buffer(16) - - def update(self, block_data): - assert len(block_data) % 16 == 0 - - result = self.ghash_c.ghash(self._last_y, - c_uint8_ptr(block_data), - c_size_t(len(block_data)), - self._last_y, - self._exp_key.get()) - if result: - raise ValueError("Error %d while updating GHASH" % result) - - return self - - def digest(self): - return get_raw_buffer(self._last_y) - - -def enum(**enums): - return type('Enum', (), enums) - - -MacStatus = enum(PROCESSING_AUTH_DATA=1, PROCESSING_CIPHERTEXT=2) - - -class GcmMode(object): - """Galois Counter Mode (GCM). - - This is an Authenticated Encryption with Associated Data (`AEAD`_) mode. - It provides both confidentiality and authenticity. - - The header of the message may be left in the clear, if needed, and it will - still be subject to authentication. The decryption step tells the receiver - if the message comes from a source that really knowns the secret key. - Additionally, decryption detects if any part of the message - including the - header - has been modified or corrupted. - - This mode requires a *nonce*. - - This mode is only available for ciphers that operate on 128 bits blocks - (e.g. AES but not TDES). - - See `NIST SP800-38D`_. - - .. _`NIST SP800-38D`: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf - .. _AEAD: http://blog.cryptographyengineering.com/2012/05/how-to-choose-authenticated-encryption.html - - :undocumented: __init__ - """ - - def __init__(self, factory, key, nonce, mac_len, cipher_params, ghash_c): - - self.block_size = factory.block_size - if self.block_size != 16: - raise ValueError("GCM mode is only available for ciphers" - " that operate on 128 bits blocks") - - if len(nonce) == 0: - raise ValueError("Nonce cannot be empty") - - if not is_buffer(nonce): - raise TypeError("Nonce must be bytes, bytearray or memoryview") - - # See NIST SP 800 38D, 5.2.1.1 - if len(nonce) > 2**64 - 1: - raise ValueError("Nonce exceeds maximum length") - - - self.nonce = _copy_bytes(None, None, nonce) - """Nonce""" - - self._factory = factory - self._key = _copy_bytes(None, None, key) - self._tag = None # Cache for MAC tag - - self._mac_len = mac_len - if not (4 <= mac_len <= 16): - raise ValueError("Parameter 'mac_len' must be in the range 4..16") - - # Allowed transitions after initialization - self._next = ["update", "encrypt", "decrypt", - "digest", "verify"] - - self._no_more_assoc_data = False - - # Length of associated data - self._auth_len = 0 - - # Length of the ciphertext or plaintext - self._msg_len = 0 - - # Step 1 in SP800-38D, Algorithm 4 (encryption) - Compute H - # See also Algorithm 5 (decryption) - hash_subkey = factory.new(key, - self._factory.MODE_ECB, - **cipher_params - ).encrypt(b'\x00' * 16) - - # Step 2 - Compute J0 - if len(self.nonce) == 12: - j0 = self.nonce + b"\x00\x00\x00\x01" - else: - fill = (16 - (len(self.nonce) % 16)) % 16 + 8 - ghash_in = (self.nonce + - b'\x00' * fill + - long_to_bytes(8 * len(self.nonce), 8)) - j0 = _GHASH(hash_subkey, ghash_c).update(ghash_in).digest() - - # Step 3 - Prepare GCTR cipher for encryption/decryption - nonce_ctr = j0[:12] - iv_ctr = (bytes_to_long(j0) + 1) & 0xFFFFFFFF - self._cipher = factory.new(key, - self._factory.MODE_CTR, - initial_value=iv_ctr, - nonce=nonce_ctr, - **cipher_params) - - # Step 5 - Bootstrat GHASH - self._signer = _GHASH(hash_subkey, ghash_c) - - # Step 6 - Prepare GCTR cipher for GMAC - self._tag_cipher = factory.new(key, - self._factory.MODE_CTR, - initial_value=j0, - nonce=b"", - **cipher_params) - - # Cache for data to authenticate - self._cache = b"" - - self._status = MacStatus.PROCESSING_AUTH_DATA - - def update(self, assoc_data): - """Protect associated data - - If there is any associated data, the caller has to invoke - this function one or more times, before using - ``decrypt`` or ``encrypt``. - - By *associated data* it is meant any data (e.g. packet headers) that - will not be encrypted and will be transmitted in the clear. - However, the receiver is still able to detect any modification to it. - In GCM, the *associated data* is also called - *additional authenticated data* (AAD). - - If there is no associated data, this method must not be called. - - The caller may split associated data in segments of any size, and - invoke this method multiple times, each time with the next segment. - - :Parameters: - assoc_data : bytes/bytearray/memoryview - A piece of associated data. There are no restrictions on its size. - """ - - if "update" not in self._next: - raise TypeError("update() can only be called" - " immediately after initialization") - - self._next = ["update", "encrypt", "decrypt", - "digest", "verify"] - - self._update(assoc_data) - self._auth_len += len(assoc_data) - - # See NIST SP 800 38D, 5.2.1.1 - if self._auth_len > 2**64 - 1: - raise ValueError("Additional Authenticated Data exceeds maximum length") - - return self - - def _update(self, data): - assert(len(self._cache) < 16) - - if len(self._cache) > 0: - filler = min(16 - len(self._cache), len(data)) - self._cache += _copy_bytes(None, filler, data) - data = data[filler:] - - if len(self._cache) < 16: - return - - # The cache is exactly one block - self._signer.update(self._cache) - self._cache = b"" - - update_len = len(data) // 16 * 16 - self._cache = _copy_bytes(update_len, None, data) - if update_len > 0: - self._signer.update(data[:update_len]) - - def _pad_cache_and_update(self): - assert(len(self._cache) < 16) - - # The authenticated data A is concatenated to the minimum - # number of zero bytes (possibly none) such that the - # - ciphertext C is aligned to the 16 byte boundary. - # See step 5 in section 7.1 - # - ciphertext C is aligned to the 16 byte boundary. - # See step 6 in section 7.2 - len_cache = len(self._cache) - if len_cache > 0: - self._update(b'\x00' * (16 - len_cache)) - - def encrypt(self, plaintext, output=None): - """Encrypt data with the key and the parameters set at initialization. - - A cipher object is stateful: once you have encrypted a message - you cannot encrypt (or decrypt) another message using the same - object. - - The data to encrypt can be broken up in two or - more pieces and `encrypt` can be called multiple times. - - That is, the statement: - - >>> c.encrypt(a) + c.encrypt(b) - - is equivalent to: - - >>> c.encrypt(a+b) - - This function does not add any padding to the plaintext. - - :Parameters: - plaintext : bytes/bytearray/memoryview - The piece of data to encrypt. - It can be of any length. - :Keywords: - output : bytearray/memoryview - The location where the ciphertext must be written to. - If ``None``, the ciphertext is returned. - :Return: - If ``output`` is ``None``, the ciphertext as ``bytes``. - Otherwise, ``None``. - """ - - if "encrypt" not in self._next: - raise TypeError("encrypt() can only be called after" - " initialization or an update()") - self._next = ["encrypt", "digest"] - - ciphertext = self._cipher.encrypt(plaintext, output=output) - - if self._status == MacStatus.PROCESSING_AUTH_DATA: - self._pad_cache_and_update() - self._status = MacStatus.PROCESSING_CIPHERTEXT - - self._update(ciphertext if output is None else output) - self._msg_len += len(plaintext) - - # See NIST SP 800 38D, 5.2.1.1 - if self._msg_len > 2**39 - 256: - raise ValueError("Plaintext exceeds maximum length") - - return ciphertext - - def decrypt(self, ciphertext, output=None): - """Decrypt data with the key and the parameters set at initialization. - - A cipher object is stateful: once you have decrypted a message - you cannot decrypt (or encrypt) another message with the same - object. - - The data to decrypt can be broken up in two or - more pieces and `decrypt` can be called multiple times. - - That is, the statement: - - >>> c.decrypt(a) + c.decrypt(b) - - is equivalent to: - - >>> c.decrypt(a+b) - - This function does not remove any padding from the plaintext. - - :Parameters: - ciphertext : bytes/bytearray/memoryview - The piece of data to decrypt. - It can be of any length. - :Keywords: - output : bytearray/memoryview - The location where the plaintext must be written to. - If ``None``, the plaintext is returned. - :Return: - If ``output`` is ``None``, the plaintext as ``bytes``. - Otherwise, ``None``. - """ - - if "decrypt" not in self._next: - raise TypeError("decrypt() can only be called" - " after initialization or an update()") - self._next = ["decrypt", "verify"] - - if self._status == MacStatus.PROCESSING_AUTH_DATA: - self._pad_cache_and_update() - self._status = MacStatus.PROCESSING_CIPHERTEXT - - self._update(ciphertext) - self._msg_len += len(ciphertext) - - return self._cipher.decrypt(ciphertext, output=output) - - def digest(self): - """Compute the *binary* MAC tag in an AEAD mode. - - The caller invokes this function at the very end. - - This method returns the MAC that shall be sent to the receiver, - together with the ciphertext. - - :Return: the MAC, as a byte string. - """ - - if "digest" not in self._next: - raise TypeError("digest() cannot be called when decrypting" - " or validating a message") - self._next = ["digest"] - - return self._compute_mac() - - def _compute_mac(self): - """Compute MAC without any FSM checks.""" - - if self._tag: - return self._tag - - # Step 5 in NIST SP 800-38D, Algorithm 4 - Compute S - self._pad_cache_and_update() - self._update(long_to_bytes(8 * self._auth_len, 8)) - self._update(long_to_bytes(8 * self._msg_len, 8)) - s_tag = self._signer.digest() - - # Step 6 - Compute T - self._tag = self._tag_cipher.encrypt(s_tag)[:self._mac_len] - - return self._tag - - def hexdigest(self): - """Compute the *printable* MAC tag. - - This method is like `digest`. - - :Return: the MAC, as a hexadecimal string. - """ - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def verify(self, received_mac_tag): - """Validate the *binary* MAC tag. - - The caller invokes this function at the very end. - - This method checks if the decrypted message is indeed valid - (that is, if the key is correct) and it has not been - tampered with while in transit. - - :Parameters: - received_mac_tag : bytes/bytearray/memoryview - This is the *binary* MAC, as received from the sender. - :Raises ValueError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - if "verify" not in self._next: - raise TypeError("verify() cannot be called" - " when encrypting a message") - self._next = ["verify"] - - secret = get_random_bytes(16) - - mac1 = BLAKE2s.new(digest_bits=160, key=secret, - data=self._compute_mac()) - mac2 = BLAKE2s.new(digest_bits=160, key=secret, - data=received_mac_tag) - - if mac1.digest() != mac2.digest(): - raise ValueError("MAC check failed") - - def hexverify(self, hex_mac_tag): - """Validate the *printable* MAC tag. - - This method is like `verify`. - - :Parameters: - hex_mac_tag : string - This is the *printable* MAC, as received from the sender. - :Raises ValueError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - self.verify(unhexlify(hex_mac_tag)) - - def encrypt_and_digest(self, plaintext, output=None): - """Perform encrypt() and digest() in one step. - - :Parameters: - plaintext : bytes/bytearray/memoryview - The piece of data to encrypt. - :Keywords: - output : bytearray/memoryview - The location where the ciphertext must be written to. - If ``None``, the ciphertext is returned. - :Return: - a tuple with two items: - - - the ciphertext, as ``bytes`` - - the MAC tag, as ``bytes`` - - The first item becomes ``None`` when the ``output`` parameter - specified a location for the result. - """ - - return self.encrypt(plaintext, output=output), self.digest() - - def decrypt_and_verify(self, ciphertext, received_mac_tag, output=None): - """Perform decrypt() and verify() in one step. - - :Parameters: - ciphertext : bytes/bytearray/memoryview - The piece of data to decrypt. - received_mac_tag : byte string - This is the *binary* MAC, as received from the sender. - :Keywords: - output : bytearray/memoryview - The location where the plaintext must be written to. - If ``None``, the plaintext is returned. - :Return: the plaintext as ``bytes`` or ``None`` when the ``output`` - parameter specified a location for the result. - :Raises ValueError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - plaintext = self.decrypt(ciphertext, output=output) - self.verify(received_mac_tag) - return plaintext - - -def _create_gcm_cipher(factory, **kwargs): - """Create a new block cipher, configured in Galois Counter Mode (GCM). - - :Parameters: - factory : module - A block cipher module, taken from `Cryptodome.Cipher`. - The cipher must have block length of 16 bytes. - GCM has been only defined for `Cryptodome.Cipher.AES`. - - :Keywords: - key : bytes/bytearray/memoryview - The secret key to use in the symmetric cipher. - It must be 16 (e.g. *AES-128*), 24 (e.g. *AES-192*) - or 32 (e.g. *AES-256*) bytes long. - - nonce : bytes/bytearray/memoryview - A value that must never be reused for any other encryption. - - There are no restrictions on its length, - but it is recommended to use at least 16 bytes. - - The nonce shall never repeat for two - different messages encrypted with the same key, - but it does not need to be random. - - If not provided, a 16 byte nonce will be randomly created. - - mac_len : integer - Length of the MAC, in bytes. - It must be no larger than 16 bytes (which is the default). - """ - - try: - key = kwargs.pop("key") - except KeyError as e: - raise TypeError("Missing parameter:" + str(e)) - - nonce = kwargs.pop("nonce", None) - if nonce is None: - nonce = get_random_bytes(16) - mac_len = kwargs.pop("mac_len", 16) - - # Not documented - only used for testing - use_clmul = kwargs.pop("use_clmul", True) - if use_clmul and _ghash_clmul: - ghash_c = _ghash_clmul - else: - ghash_c = _ghash_portable - - return GcmMode(factory, key, nonce, mac_len, kwargs, ghash_c) diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_gcm.pyi b/resources/lib/deps/Cryptodome/Cipher/_mode_gcm.pyi deleted file mode 100644 index 8912955..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_gcm.pyi +++ /dev/null @@ -1,45 +0,0 @@ -from types import ModuleType -from typing import Union, Tuple, Dict, overload, Optional - -__all__ = ['GcmMode'] - -Buffer = Union[bytes, bytearray, memoryview] - -class GcmMode(object): - block_size: int - nonce: Buffer - - def __init__(self, - factory: ModuleType, - key: Buffer, - nonce: Buffer, - mac_len: int, - cipher_params: Dict) -> None: ... - - def update(self, assoc_data: Buffer) -> GcmMode: ... - - @overload - def encrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def encrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - @overload - def decrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def decrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def verify(self, received_mac_tag: Buffer) -> None: ... - def hexverify(self, hex_mac_tag: str) -> None: ... - - @overload - def encrypt_and_digest(self, - plaintext: Buffer) -> Tuple[bytes, bytes]: ... - @overload - def encrypt_and_digest(self, - plaintext: Buffer, - output: Buffer) -> Tuple[None, bytes]: ... - def decrypt_and_verify(self, - ciphertext: Buffer, - received_mac_tag: Buffer, - output: Optional[Union[bytearray, memoryview]] = ...) -> bytes: ... diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_ocb.py b/resources/lib/deps/Cryptodome/Cipher/_mode_ocb.py deleted file mode 100644 index 1295e61..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_ocb.py +++ /dev/null @@ -1,532 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -""" -Offset Codebook (OCB) mode. - -OCB is Authenticated Encryption with Associated Data (AEAD) cipher mode -designed by Prof. Phillip Rogaway and specified in `RFC7253`_. - -The algorithm provides both authenticity and privacy, it is very efficient, -it uses only one key and it can be used in online mode (so that encryption -or decryption can start before the end of the message is available). - -This module implements the third and last variant of OCB (OCB3) and it only -works in combination with a 128-bit block symmetric cipher, like AES. - -OCB is patented in US but `free licenses`_ exist for software implementations -meant for non-military purposes. - -Example: - >>> from Cryptodome.Cipher import AES - >>> from Cryptodome.Random import get_random_bytes - >>> - >>> key = get_random_bytes(32) - >>> cipher = AES.new(key, AES.MODE_OCB) - >>> plaintext = b"Attack at dawn" - >>> ciphertext, mac = cipher.encrypt_and_digest(plaintext) - >>> # Deliver cipher.nonce, ciphertext and mac - ... - >>> cipher = AES.new(key, AES.MODE_OCB, nonce=nonce) - >>> try: - >>> plaintext = cipher.decrypt_and_verify(ciphertext, mac) - >>> except ValueError: - >>> print "Invalid message" - >>> else: - >>> print plaintext - -:undocumented: __package__ - -.. _RFC7253: http://www.rfc-editor.org/info/rfc7253 -.. _free licenses: http://web.cs.ucdavis.edu/~rogaway/ocb/license.htm -""" - -import struct -from binascii import unhexlify - -from Cryptodome.Util.py3compat import bord, _copy_bytes, bchr -from Cryptodome.Util.number import long_to_bytes, bytes_to_long -from Cryptodome.Util.strxor import strxor - -from Cryptodome.Hash import BLAKE2s -from Cryptodome.Random import get_random_bytes - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, - create_string_buffer, get_raw_buffer, - SmartPointer, c_size_t, c_uint8_ptr, - is_buffer) - -_raw_ocb_lib = load_pycryptodome_raw_lib("Cryptodome.Cipher._raw_ocb", """ - int OCB_start_operation(void *cipher, - const uint8_t *offset_0, - size_t offset_0_len, - void **pState); - int OCB_encrypt(void *state, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int OCB_decrypt(void *state, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int OCB_update(void *state, - const uint8_t *in, - size_t data_len); - int OCB_digest(void *state, - uint8_t *tag, - size_t tag_len); - int OCB_stop_operation(void *state); - """) - - -class OcbMode(object): - """Offset Codebook (OCB) mode. - - :undocumented: __init__ - """ - - def __init__(self, factory, nonce, mac_len, cipher_params): - - if factory.block_size != 16: - raise ValueError("OCB mode is only available for ciphers" - " that operate on 128 bits blocks") - - self.block_size = 16 - """The block size of the underlying cipher, in bytes.""" - - self.nonce = _copy_bytes(None, None, nonce) - """Nonce used for this session.""" - if len(nonce) not in range(1, 16): - raise ValueError("Nonce must be at most 15 bytes long") - if not is_buffer(nonce): - raise TypeError("Nonce must be bytes, bytearray or memoryview") - - self._mac_len = mac_len - if not 8 <= mac_len <= 16: - raise ValueError("MAC tag must be between 8 and 16 bytes long") - - # Cache for MAC tag - self._mac_tag = None - - # Cache for unaligned associated data - self._cache_A = b"" - - # Cache for unaligned ciphertext/plaintext - self._cache_P = b"" - - # Allowed transitions after initialization - self._next = ["update", "encrypt", "decrypt", - "digest", "verify"] - - # Compute Offset_0 - params_without_key = dict(cipher_params) - key = params_without_key.pop("key") - - taglen_mod128 = (self._mac_len * 8) % 128 - if len(self.nonce) < 15: - nonce = bchr(taglen_mod128 << 1) +\ - b'\x00' * (14 - len(nonce)) +\ - b'\x01' +\ - self.nonce - else: - nonce = bchr((taglen_mod128 << 1) | 0x01) +\ - self.nonce - - bottom_bits = bord(nonce[15]) & 0x3F # 6 bits, 0..63 - top_bits = bord(nonce[15]) & 0xC0 # 2 bits - - ktop_cipher = factory.new(key, - factory.MODE_ECB, - **params_without_key) - ktop = ktop_cipher.encrypt(struct.pack('15sB', - nonce[:15], - top_bits)) - - stretch = ktop + strxor(ktop[:8], ktop[1:9]) # 192 bits - offset_0 = long_to_bytes(bytes_to_long(stretch) >> - (64 - bottom_bits), 24)[8:] - - # Create low-level cipher instance - raw_cipher = factory._create_base_cipher(cipher_params) - if cipher_params: - raise TypeError("Unknown keywords: " + str(cipher_params)) - - self._state = VoidPointer() - result = _raw_ocb_lib.OCB_start_operation(raw_cipher.get(), - offset_0, - c_size_t(len(offset_0)), - self._state.address_of()) - if result: - raise ValueError("Error %d while instantiating the OCB mode" - % result) - - # Ensure that object disposal of this Python object will (eventually) - # free the memory allocated by the raw library for the cipher mode - self._state = SmartPointer(self._state.get(), - _raw_ocb_lib.OCB_stop_operation) - - # Memory allocated for the underlying block cipher is now owed - # by the cipher mode - raw_cipher.release() - - def _update(self, assoc_data, assoc_data_len): - result = _raw_ocb_lib.OCB_update(self._state.get(), - c_uint8_ptr(assoc_data), - c_size_t(assoc_data_len)) - if result: - raise ValueError("Error %d while computing MAC in OCB mode" % result) - - def update(self, assoc_data): - """Process the associated data. - - If there is any associated data, the caller has to invoke - this method one or more times, before using - ``decrypt`` or ``encrypt``. - - By *associated data* it is meant any data (e.g. packet headers) that - will not be encrypted and will be transmitted in the clear. - However, the receiver shall still able to detect modifications. - - If there is no associated data, this method must not be called. - - The caller may split associated data in segments of any size, and - invoke this method multiple times, each time with the next segment. - - :Parameters: - assoc_data : bytes/bytearray/memoryview - A piece of associated data. - """ - - if "update" not in self._next: - raise TypeError("update() can only be called" - " immediately after initialization") - - self._next = ["encrypt", "decrypt", "digest", - "verify", "update"] - - if len(self._cache_A) > 0: - filler = min(16 - len(self._cache_A), len(assoc_data)) - self._cache_A += _copy_bytes(None, filler, assoc_data) - assoc_data = assoc_data[filler:] - - if len(self._cache_A) < 16: - return self - - # Clear the cache, and proceeding with any other aligned data - self._cache_A, seg = b"", self._cache_A - self.update(seg) - - update_len = len(assoc_data) // 16 * 16 - self._cache_A = _copy_bytes(update_len, None, assoc_data) - self._update(assoc_data, update_len) - return self - - def _transcrypt_aligned(self, in_data, in_data_len, - trans_func, trans_desc): - - out_data = create_string_buffer(in_data_len) - result = trans_func(self._state.get(), - in_data, - out_data, - c_size_t(in_data_len)) - if result: - raise ValueError("Error %d while %sing in OCB mode" - % (result, trans_desc)) - return get_raw_buffer(out_data) - - def _transcrypt(self, in_data, trans_func, trans_desc): - # Last piece to encrypt/decrypt - if in_data is None: - out_data = self._transcrypt_aligned(self._cache_P, - len(self._cache_P), - trans_func, - trans_desc) - self._cache_P = b"" - return out_data - - # Try to fill up the cache, if it already contains something - prefix = b"" - if len(self._cache_P) > 0: - filler = min(16 - len(self._cache_P), len(in_data)) - self._cache_P += _copy_bytes(None, filler, in_data) - in_data = in_data[filler:] - - if len(self._cache_P) < 16: - # We could not manage to fill the cache, so there is certainly - # no output yet. - return b"" - - # Clear the cache, and proceeding with any other aligned data - prefix = self._transcrypt_aligned(self._cache_P, - len(self._cache_P), - trans_func, - trans_desc) - self._cache_P = b"" - - # Process data in multiples of the block size - trans_len = len(in_data) // 16 * 16 - result = self._transcrypt_aligned(c_uint8_ptr(in_data), - trans_len, - trans_func, - trans_desc) - if prefix: - result = prefix + result - - # Left-over - self._cache_P = _copy_bytes(trans_len, None, in_data) - - return result - - def encrypt(self, plaintext=None): - """Encrypt the next piece of plaintext. - - After the entire plaintext has been passed (but before `digest`), - you **must** call this method one last time with no arguments to collect - the final piece of ciphertext. - - If possible, use the method `encrypt_and_digest` instead. - - :Parameters: - plaintext : bytes/bytearray/memoryview - The next piece of data to encrypt or ``None`` to signify - that encryption has finished and that any remaining ciphertext - has to be produced. - :Return: - the ciphertext, as a byte string. - Its length may not match the length of the *plaintext*. - """ - - if "encrypt" not in self._next: - raise TypeError("encrypt() can only be called after" - " initialization or an update()") - - if plaintext is None: - self._next = ["digest"] - else: - self._next = ["encrypt"] - return self._transcrypt(plaintext, _raw_ocb_lib.OCB_encrypt, "encrypt") - - def decrypt(self, ciphertext=None): - """Decrypt the next piece of ciphertext. - - After the entire ciphertext has been passed (but before `verify`), - you **must** call this method one last time with no arguments to collect - the remaining piece of plaintext. - - If possible, use the method `decrypt_and_verify` instead. - - :Parameters: - ciphertext : bytes/bytearray/memoryview - The next piece of data to decrypt or ``None`` to signify - that decryption has finished and that any remaining plaintext - has to be produced. - :Return: - the plaintext, as a byte string. - Its length may not match the length of the *ciphertext*. - """ - - if "decrypt" not in self._next: - raise TypeError("decrypt() can only be called after" - " initialization or an update()") - - if ciphertext is None: - self._next = ["verify"] - else: - self._next = ["decrypt"] - return self._transcrypt(ciphertext, - _raw_ocb_lib.OCB_decrypt, - "decrypt") - - def _compute_mac_tag(self): - - if self._mac_tag is not None: - return - - if self._cache_A: - self._update(self._cache_A, len(self._cache_A)) - self._cache_A = b"" - - mac_tag = create_string_buffer(16) - result = _raw_ocb_lib.OCB_digest(self._state.get(), - mac_tag, - c_size_t(len(mac_tag)) - ) - if result: - raise ValueError("Error %d while computing digest in OCB mode" - % result) - self._mac_tag = get_raw_buffer(mac_tag)[:self._mac_len] - - def digest(self): - """Compute the *binary* MAC tag. - - Call this method after the final `encrypt` (the one with no arguments) - to obtain the MAC tag. - - The MAC tag is needed by the receiver to determine authenticity - of the message. - - :Return: the MAC, as a byte string. - """ - - if "digest" not in self._next: - raise TypeError("digest() cannot be called now for this cipher") - - assert(len(self._cache_P) == 0) - - self._next = ["digest"] - - if self._mac_tag is None: - self._compute_mac_tag() - - return self._mac_tag - - def hexdigest(self): - """Compute the *printable* MAC tag. - - This method is like `digest`. - - :Return: the MAC, as a hexadecimal string. - """ - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def verify(self, received_mac_tag): - """Validate the *binary* MAC tag. - - Call this method after the final `decrypt` (the one with no arguments) - to check if the message is authentic and valid. - - :Parameters: - received_mac_tag : bytes/bytearray/memoryview - This is the *binary* MAC, as received from the sender. - :Raises ValueError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - if "verify" not in self._next: - raise TypeError("verify() cannot be called now for this cipher") - - assert(len(self._cache_P) == 0) - - self._next = ["verify"] - - if self._mac_tag is None: - self._compute_mac_tag() - - secret = get_random_bytes(16) - mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=self._mac_tag) - mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=received_mac_tag) - - if mac1.digest() != mac2.digest(): - raise ValueError("MAC check failed") - - def hexverify(self, hex_mac_tag): - """Validate the *printable* MAC tag. - - This method is like `verify`. - - :Parameters: - hex_mac_tag : string - This is the *printable* MAC, as received from the sender. - :Raises ValueError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - self.verify(unhexlify(hex_mac_tag)) - - def encrypt_and_digest(self, plaintext): - """Encrypt the message and create the MAC tag in one step. - - :Parameters: - plaintext : bytes/bytearray/memoryview - The entire message to encrypt. - :Return: - a tuple with two byte strings: - - - the encrypted data - - the MAC - """ - - return self.encrypt(plaintext) + self.encrypt(), self.digest() - - def decrypt_and_verify(self, ciphertext, received_mac_tag): - """Decrypted the message and verify its authenticity in one step. - - :Parameters: - ciphertext : bytes/bytearray/memoryview - The entire message to decrypt. - received_mac_tag : byte string - This is the *binary* MAC, as received from the sender. - - :Return: the decrypted data (byte string). - :Raises ValueError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - plaintext = self.decrypt(ciphertext) + self.decrypt() - self.verify(received_mac_tag) - return plaintext - - -def _create_ocb_cipher(factory, **kwargs): - """Create a new block cipher, configured in OCB mode. - - :Parameters: - factory : module - A symmetric cipher module from `Cryptodome.Cipher` - (like `Cryptodome.Cipher.AES`). - - :Keywords: - nonce : bytes/bytearray/memoryview - A value that must never be reused for any other encryption. - Its length can vary from 1 to 15 bytes. - If not specified, a random 15 bytes long nonce is generated. - - mac_len : integer - Length of the MAC, in bytes. - It must be in the range ``[8..16]``. - The default is 16 (128 bits). - - Any other keyword will be passed to the underlying block cipher. - See the relevant documentation for details (at least ``key`` will need - to be present). - """ - - try: - nonce = kwargs.pop("nonce", None) - if nonce is None: - nonce = get_random_bytes(15) - mac_len = kwargs.pop("mac_len", 16) - except KeyError as e: - raise TypeError("Keyword missing: " + str(e)) - - return OcbMode(factory, nonce, mac_len, kwargs) diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_ocb.pyi b/resources/lib/deps/Cryptodome/Cipher/_mode_ocb.pyi deleted file mode 100644 index a1909fc..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_ocb.pyi +++ /dev/null @@ -1,36 +0,0 @@ -from types import ModuleType -from typing import Union, Any, Optional, Tuple, Dict, overload - -Buffer = Union[bytes, bytearray, memoryview] - -class OcbMode(object): - block_size: int - nonce: Buffer - - def __init__(self, - factory: ModuleType, - nonce: Buffer, - mac_len: int, - cipher_params: Dict) -> None: ... - - def update(self, assoc_data: Buffer) -> OcbMode: ... - - @overload - def encrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def encrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - @overload - def decrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def decrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def verify(self, received_mac_tag: Buffer) -> None: ... - def hexverify(self, hex_mac_tag: str) -> None: ... - - def encrypt_and_digest(self, - plaintext: Buffer) -> Tuple[bytes, bytes]: ... - def decrypt_and_verify(self, - ciphertext: Buffer, - received_mac_tag: Buffer) -> bytes: ... diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_ofb.py b/resources/lib/deps/Cryptodome/Cipher/_mode_ofb.py deleted file mode 100644 index 8c0ccf6..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_ofb.py +++ /dev/null @@ -1,282 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Cipher/mode_ofb.py : OFB mode -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -""" -Output Feedback (CFB) mode. -""" - -__all__ = ['OfbMode'] - -from Cryptodome.Util.py3compat import _copy_bytes -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, - create_string_buffer, get_raw_buffer, - SmartPointer, c_size_t, c_uint8_ptr, - is_writeable_buffer) - -from Cryptodome.Random import get_random_bytes - -raw_ofb_lib = load_pycryptodome_raw_lib("Cryptodome.Cipher._raw_ofb", """ - int OFB_start_operation(void *cipher, - const uint8_t iv[], - size_t iv_len, - void **pResult); - int OFB_encrypt(void *ofbState, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int OFB_decrypt(void *ofbState, - const uint8_t *in, - uint8_t *out, - size_t data_len); - int OFB_stop_operation(void *state); - """ - ) - - -class OfbMode(object): - """*Output FeedBack (OFB)*. - - This mode is very similar to CBC, but it - transforms the underlying block cipher into a stream cipher. - - The keystream is the iterated block encryption of the - previous ciphertext block. - - An Initialization Vector (*IV*) is required. - - See `NIST SP800-38A`_ , Section 6.4. - - .. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf - - :undocumented: __init__ - """ - - def __init__(self, block_cipher, iv): - """Create a new block cipher, configured in OFB mode. - - :Parameters: - block_cipher : C pointer - A smart pointer to the low-level block cipher instance. - - iv : bytes/bytearray/memoryview - The initialization vector to use for encryption or decryption. - It is as long as the cipher block. - - **The IV must be a nonce, to to be reused for any other - message**. It shall be a nonce or a random value. - - Reusing the *IV* for encryptions performed with the same key - compromises confidentiality. - """ - - self._state = VoidPointer() - result = raw_ofb_lib.OFB_start_operation(block_cipher.get(), - c_uint8_ptr(iv), - c_size_t(len(iv)), - self._state.address_of()) - if result: - raise ValueError("Error %d while instantiating the OFB mode" - % result) - - # Ensure that object disposal of this Python object will (eventually) - # free the memory allocated by the raw library for the cipher mode - self._state = SmartPointer(self._state.get(), - raw_ofb_lib.OFB_stop_operation) - - # Memory allocated for the underlying block cipher is now owed - # by the cipher mode - block_cipher.release() - - self.block_size = len(iv) - """The block size of the underlying cipher, in bytes.""" - - self.iv = _copy_bytes(None, None, iv) - """The Initialization Vector originally used to create the object. - The value does not change.""" - - self.IV = self.iv - """Alias for `iv`""" - - self._next = ["encrypt", "decrypt"] - - def encrypt(self, plaintext, output=None): - """Encrypt data with the key and the parameters set at initialization. - - A cipher object is stateful: once you have encrypted a message - you cannot encrypt (or decrypt) another message using the same - object. - - The data to encrypt can be broken up in two or - more pieces and `encrypt` can be called multiple times. - - That is, the statement: - - >>> c.encrypt(a) + c.encrypt(b) - - is equivalent to: - - >>> c.encrypt(a+b) - - This function does not add any padding to the plaintext. - - :Parameters: - plaintext : bytes/bytearray/memoryview - The piece of data to encrypt. - It can be of any length. - :Keywords: - output : bytearray/memoryview - The location where the ciphertext must be written to. - If ``None``, the ciphertext is returned. - :Return: - If ``output`` is ``None``, the ciphertext is returned as ``bytes``. - Otherwise, ``None``. - """ - - if "encrypt" not in self._next: - raise TypeError("encrypt() cannot be called after decrypt()") - self._next = ["encrypt"] - - if output is None: - ciphertext = create_string_buffer(len(plaintext)) - else: - ciphertext = output - - if not is_writeable_buffer(output): - raise TypeError("output must be a bytearray or a writeable memoryview") - - if len(plaintext) != len(output): - raise ValueError("output must have the same length as the input" - " (%d bytes)" % len(plaintext)) - - result = raw_ofb_lib.OFB_encrypt(self._state.get(), - c_uint8_ptr(plaintext), - c_uint8_ptr(ciphertext), - c_size_t(len(plaintext))) - if result: - raise ValueError("Error %d while encrypting in OFB mode" % result) - - if output is None: - return get_raw_buffer(ciphertext) - else: - return None - - def decrypt(self, ciphertext, output=None): - """Decrypt data with the key and the parameters set at initialization. - - A cipher object is stateful: once you have decrypted a message - you cannot decrypt (or encrypt) another message with the same - object. - - The data to decrypt can be broken up in two or - more pieces and `decrypt` can be called multiple times. - - That is, the statement: - - >>> c.decrypt(a) + c.decrypt(b) - - is equivalent to: - - >>> c.decrypt(a+b) - - This function does not remove any padding from the plaintext. - - :Parameters: - ciphertext : bytes/bytearray/memoryview - The piece of data to decrypt. - It can be of any length. - :Keywords: - output : bytearray/memoryview - The location where the plaintext is written to. - If ``None``, the plaintext is returned. - :Return: - If ``output`` is ``None``, the plaintext is returned as ``bytes``. - Otherwise, ``None``. - """ - - if "decrypt" not in self._next: - raise TypeError("decrypt() cannot be called after encrypt()") - self._next = ["decrypt"] - - if output is None: - plaintext = create_string_buffer(len(ciphertext)) - else: - plaintext = output - - if not is_writeable_buffer(output): - raise TypeError("output must be a bytearray or a writeable memoryview") - - if len(ciphertext) != len(output): - raise ValueError("output must have the same length as the input" - " (%d bytes)" % len(plaintext)) - - result = raw_ofb_lib.OFB_decrypt(self._state.get(), - c_uint8_ptr(ciphertext), - c_uint8_ptr(plaintext), - c_size_t(len(ciphertext))) - if result: - raise ValueError("Error %d while decrypting in OFB mode" % result) - - if output is None: - return get_raw_buffer(plaintext) - else: - return None - - -def _create_ofb_cipher(factory, **kwargs): - """Instantiate a cipher object that performs OFB encryption/decryption. - - :Parameters: - factory : module - The underlying block cipher, a module from ``Cryptodome.Cipher``. - - :Keywords: - iv : bytes/bytearray/memoryview - The IV to use for OFB. - - IV : bytes/bytearray/memoryview - Alias for ``iv``. - - Any other keyword will be passed to the underlying block cipher. - See the relevant documentation for details (at least ``key`` will need - to be present). - """ - - cipher_state = factory._create_base_cipher(kwargs) - iv = kwargs.pop("IV", None) - IV = kwargs.pop("iv", None) - - if (None, None) == (iv, IV): - iv = get_random_bytes(factory.block_size) - if iv is not None: - if IV is not None: - raise TypeError("You must either use 'iv' or 'IV', not both") - else: - iv = IV - - if len(iv) != factory.block_size: - raise ValueError("Incorrect IV length (it must be %d bytes long)" % - factory.block_size) - - if kwargs: - raise TypeError("Unknown parameters for OFB: %s" % str(kwargs)) - - return OfbMode(cipher_state, iv) diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_ofb.pyi b/resources/lib/deps/Cryptodome/Cipher/_mode_ofb.pyi deleted file mode 100644 index d28608e..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_ofb.pyi +++ /dev/null @@ -1,25 +0,0 @@ -from typing import Union, overload - -from Cryptodome.Util._raw_api import SmartPointer - -Buffer = Union[bytes, bytearray, memoryview] - -__all__ = ['OfbMode'] - -class OfbMode(object): - block_size: int - iv: Buffer - IV: Buffer - - def __init__(self, - block_cipher: SmartPointer, - iv: Buffer) -> None: ... - @overload - def encrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def encrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - @overload - def decrypt(self, plaintext: Buffer) -> bytes: ... - @overload - def decrypt(self, plaintext: Buffer, output: Union[bytearray, memoryview]) -> None: ... - diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_openpgp.py b/resources/lib/deps/Cryptodome/Cipher/_mode_openpgp.py deleted file mode 100644 index d86ed19..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_openpgp.py +++ /dev/null @@ -1,206 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -""" -OpenPGP mode. -""" - -__all__ = ['OpenPgpMode'] - -from Cryptodome.Util.py3compat import _copy_bytes -from Cryptodome.Random import get_random_bytes - -class OpenPgpMode(object): - """OpenPGP mode. - - This mode is a variant of CFB, and it is only used in PGP and - OpenPGP_ applications. If in doubt, use another mode. - - An Initialization Vector (*IV*) is required. - - Unlike CFB, the *encrypted* IV (not the IV itself) is - transmitted to the receiver. - - The IV is a random data block. For legacy reasons, two of its bytes are - duplicated to act as a checksum for the correctness of the key, which is now - known to be insecure and is ignored. The encrypted IV is therefore 2 bytes - longer than the clean IV. - - .. _OpenPGP: http://tools.ietf.org/html/rfc4880 - - :undocumented: __init__ - """ - - def __init__(self, factory, key, iv, cipher_params): - - #: The block size of the underlying cipher, in bytes. - self.block_size = factory.block_size - - self._done_first_block = False # True after the first encryption - - # Instantiate a temporary cipher to process the IV - IV_cipher = factory.new( - key, - factory.MODE_CFB, - IV=b'\x00' * self.block_size, - segment_size=self.block_size * 8, - **cipher_params) - - iv = _copy_bytes(None, None, iv) - - # The cipher will be used for... - if len(iv) == self.block_size: - # ... encryption - self._encrypted_IV = IV_cipher.encrypt(iv + iv[-2:]) - elif len(iv) == self.block_size + 2: - # ... decryption - self._encrypted_IV = iv - # Last two bytes are for a deprecated "quick check" feature that - # should not be used. (https://eprint.iacr.org/2005/033) - iv = IV_cipher.decrypt(iv)[:-2] - else: - raise ValueError("Length of IV must be %d or %d bytes" - " for MODE_OPENPGP" - % (self.block_size, self.block_size + 2)) - - self.iv = self.IV = iv - - # Instantiate the cipher for the real PGP data - self._cipher = factory.new( - key, - factory.MODE_CFB, - IV=self._encrypted_IV[-self.block_size:], - segment_size=self.block_size * 8, - **cipher_params) - - def encrypt(self, plaintext): - """Encrypt data with the key and the parameters set at initialization. - - A cipher object is stateful: once you have encrypted a message - you cannot encrypt (or decrypt) another message using the same - object. - - The data to encrypt can be broken up in two or - more pieces and `encrypt` can be called multiple times. - - That is, the statement: - - >>> c.encrypt(a) + c.encrypt(b) - - is equivalent to: - - >>> c.encrypt(a+b) - - This function does not add any padding to the plaintext. - - :Parameters: - plaintext : bytes/bytearray/memoryview - The piece of data to encrypt. - - :Return: - the encrypted data, as a byte string. - It is as long as *plaintext* with one exception: - when encrypting the first message chunk, - the encypted IV is prepended to the returned ciphertext. - """ - - res = self._cipher.encrypt(plaintext) - if not self._done_first_block: - res = self._encrypted_IV + res - self._done_first_block = True - return res - - def decrypt(self, ciphertext): - """Decrypt data with the key and the parameters set at initialization. - - A cipher object is stateful: once you have decrypted a message - you cannot decrypt (or encrypt) another message with the same - object. - - The data to decrypt can be broken up in two or - more pieces and `decrypt` can be called multiple times. - - That is, the statement: - - >>> c.decrypt(a) + c.decrypt(b) - - is equivalent to: - - >>> c.decrypt(a+b) - - This function does not remove any padding from the plaintext. - - :Parameters: - ciphertext : bytes/bytearray/memoryview - The piece of data to decrypt. - - :Return: the decrypted data (byte string). - """ - - return self._cipher.decrypt(ciphertext) - - -def _create_openpgp_cipher(factory, **kwargs): - """Create a new block cipher, configured in OpenPGP mode. - - :Parameters: - factory : module - The module. - - :Keywords: - key : bytes/bytearray/memoryview - The secret key to use in the symmetric cipher. - - IV : bytes/bytearray/memoryview - The initialization vector to use for encryption or decryption. - - For encryption, the IV must be as long as the cipher block size. - - For decryption, it must be 2 bytes longer (it is actually the - *encrypted* IV which was prefixed to the ciphertext). - """ - - iv = kwargs.pop("IV", None) - IV = kwargs.pop("iv", None) - - if (None, None) == (iv, IV): - iv = get_random_bytes(factory.block_size) - if iv is not None: - if IV is not None: - raise TypeError("You must either use 'iv' or 'IV', not both") - else: - iv = IV - - try: - key = kwargs.pop("key") - except KeyError as e: - raise TypeError("Missing component: " + str(e)) - - return OpenPgpMode(factory, key, iv, kwargs) diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_openpgp.pyi b/resources/lib/deps/Cryptodome/Cipher/_mode_openpgp.pyi deleted file mode 100644 index 14b8105..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_openpgp.pyi +++ /dev/null @@ -1,20 +0,0 @@ -from types import ModuleType -from typing import Union, Dict - -Buffer = Union[bytes, bytearray, memoryview] - -__all__ = ['OpenPgpMode'] - -class OpenPgpMode(object): - block_size: int - iv: Union[bytes, bytearray, memoryview] - IV: Union[bytes, bytearray, memoryview] - - def __init__(self, - factory: ModuleType, - key: Buffer, - iv: Buffer, - cipher_params: Dict) -> None: ... - def encrypt(self, plaintext: Buffer) -> bytes: ... - def decrypt(self, plaintext: Buffer) -> bytes: ... - diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_siv.py b/resources/lib/deps/Cryptodome/Cipher/_mode_siv.py deleted file mode 100644 index 4a76ad6..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_siv.py +++ /dev/null @@ -1,392 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -""" -Synthetic Initialization Vector (SIV) mode. -""" - -__all__ = ['SivMode'] - -from binascii import hexlify, unhexlify - -from Cryptodome.Util.py3compat import bord, _copy_bytes - -from Cryptodome.Util._raw_api import is_buffer - -from Cryptodome.Util.number import long_to_bytes, bytes_to_long -from Cryptodome.Protocol.KDF import _S2V -from Cryptodome.Hash import BLAKE2s -from Cryptodome.Random import get_random_bytes - - -class SivMode(object): - """Synthetic Initialization Vector (SIV). - - This is an Authenticated Encryption with Associated Data (`AEAD`_) mode. - It provides both confidentiality and authenticity. - - The header of the message may be left in the clear, if needed, and it will - still be subject to authentication. The decryption step tells the receiver - if the message comes from a source that really knowns the secret key. - Additionally, decryption detects if any part of the message - including the - header - has been modified or corrupted. - - Unlike other AEAD modes such as CCM, EAX or GCM, accidental reuse of a - nonce is not catastrophic for the confidentiality of the message. The only - effect is that an attacker can tell when the same plaintext (and same - associated data) is protected with the same key. - - The length of the MAC is fixed to the block size of the underlying cipher. - The key size is twice the length of the key of the underlying cipher. - - This mode is only available for AES ciphers. - - +--------------------+---------------+-------------------+ - | Cipher | SIV MAC size | SIV key length | - | | (bytes) | (bytes) | - +====================+===============+===================+ - | AES-128 | 16 | 32 | - +--------------------+---------------+-------------------+ - | AES-192 | 16 | 48 | - +--------------------+---------------+-------------------+ - | AES-256 | 16 | 64 | - +--------------------+---------------+-------------------+ - - See `RFC5297`_ and the `original paper`__. - - .. _RFC5297: https://tools.ietf.org/html/rfc5297 - .. _AEAD: http://blog.cryptographyengineering.com/2012/05/how-to-choose-authenticated-encryption.html - .. __: http://www.cs.ucdavis.edu/~rogaway/papers/keywrap.pdf - - :undocumented: __init__ - """ - - def __init__(self, factory, key, nonce, kwargs): - - self.block_size = factory.block_size - """The block size of the underlying cipher, in bytes.""" - - self._factory = factory - - self._cipher_params = kwargs - - if len(key) not in (32, 48, 64): - raise ValueError("Incorrect key length (%d bytes)" % len(key)) - - if nonce is not None: - if not is_buffer(nonce): - raise TypeError("When provided, the nonce must be bytes, bytearray or memoryview") - - if len(nonce) == 0: - raise ValueError("When provided, the nonce must be non-empty") - - self.nonce = _copy_bytes(None, None, nonce) - """Public attribute is only available in case of non-deterministic - encryption.""" - - subkey_size = len(key) // 2 - - self._mac_tag = None # Cache for MAC tag - self._kdf = _S2V(key[:subkey_size], - ciphermod=factory, - cipher_params=self._cipher_params) - self._subkey_cipher = key[subkey_size:] - - # Purely for the purpose of verifying that cipher_params are OK - factory.new(key[:subkey_size], factory.MODE_ECB, **kwargs) - - # Allowed transitions after initialization - self._next = ["update", "encrypt", "decrypt", - "digest", "verify"] - - def _create_ctr_cipher(self, v): - """Create a new CTR cipher from V in SIV mode""" - - v_int = bytes_to_long(v) - q = v_int & 0xFFFFFFFFFFFFFFFF7FFFFFFF7FFFFFFF - return self._factory.new( - self._subkey_cipher, - self._factory.MODE_CTR, - initial_value=q, - nonce=b"", - **self._cipher_params) - - def update(self, component): - """Protect one associated data component - - For SIV, the associated data is a sequence (*vector*) of non-empty - byte strings (*components*). - - This method consumes the next component. It must be called - once for each of the components that constitue the associated data. - - Note that the components have clear boundaries, so that: - - >>> cipher.update(b"builtin") - >>> cipher.update(b"securely") - - is not equivalent to: - - >>> cipher.update(b"built") - >>> cipher.update(b"insecurely") - - If there is no associated data, this method must not be called. - - :Parameters: - component : bytes/bytearray/memoryview - The next associated data component. - """ - - if "update" not in self._next: - raise TypeError("update() can only be called" - " immediately after initialization") - - self._next = ["update", "encrypt", "decrypt", - "digest", "verify"] - - return self._kdf.update(component) - - def encrypt(self, plaintext): - """ - For SIV, encryption and MAC authentication must take place at the same - point. This method shall not be used. - - Use `encrypt_and_digest` instead. - """ - - raise TypeError("encrypt() not allowed for SIV mode." - " Use encrypt_and_digest() instead.") - - def decrypt(self, ciphertext): - """ - For SIV, decryption and verification must take place at the same - point. This method shall not be used. - - Use `decrypt_and_verify` instead. - """ - - raise TypeError("decrypt() not allowed for SIV mode." - " Use decrypt_and_verify() instead.") - - def digest(self): - """Compute the *binary* MAC tag. - - The caller invokes this function at the very end. - - This method returns the MAC that shall be sent to the receiver, - together with the ciphertext. - - :Return: the MAC, as a byte string. - """ - - if "digest" not in self._next: - raise TypeError("digest() cannot be called when decrypting" - " or validating a message") - self._next = ["digest"] - if self._mac_tag is None: - self._mac_tag = self._kdf.derive() - return self._mac_tag - - def hexdigest(self): - """Compute the *printable* MAC tag. - - This method is like `digest`. - - :Return: the MAC, as a hexadecimal string. - """ - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def verify(self, received_mac_tag): - """Validate the *binary* MAC tag. - - The caller invokes this function at the very end. - - This method checks if the decrypted message is indeed valid - (that is, if the key is correct) and it has not been - tampered with while in transit. - - :Parameters: - received_mac_tag : bytes/bytearray/memoryview - This is the *binary* MAC, as received from the sender. - :Raises ValueError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - if "verify" not in self._next: - raise TypeError("verify() cannot be called" - " when encrypting a message") - self._next = ["verify"] - - if self._mac_tag is None: - self._mac_tag = self._kdf.derive() - - secret = get_random_bytes(16) - - mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=self._mac_tag) - mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=received_mac_tag) - - if mac1.digest() != mac2.digest(): - raise ValueError("MAC check failed") - - def hexverify(self, hex_mac_tag): - """Validate the *printable* MAC tag. - - This method is like `verify`. - - :Parameters: - hex_mac_tag : string - This is the *printable* MAC, as received from the sender. - :Raises ValueError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - self.verify(unhexlify(hex_mac_tag)) - - def encrypt_and_digest(self, plaintext, output=None): - """Perform encrypt() and digest() in one step. - - :Parameters: - plaintext : bytes/bytearray/memoryview - The piece of data to encrypt. - :Keywords: - output : bytearray/memoryview - The location where the ciphertext must be written to. - If ``None``, the ciphertext is returned. - :Return: - a tuple with two items: - - - the ciphertext, as ``bytes`` - - the MAC tag, as ``bytes`` - - The first item becomes ``None`` when the ``output`` parameter - specified a location for the result. - """ - - if "encrypt" not in self._next: - raise TypeError("encrypt() can only be called after" - " initialization or an update()") - - self._next = ["digest"] - - # Compute V (MAC) - if hasattr(self, 'nonce'): - self._kdf.update(self.nonce) - self._kdf.update(plaintext) - self._mac_tag = self._kdf.derive() - - cipher = self._create_ctr_cipher(self._mac_tag) - - return cipher.encrypt(plaintext, output=output), self._mac_tag - - def decrypt_and_verify(self, ciphertext, mac_tag, output=None): - """Perform decryption and verification in one step. - - A cipher object is stateful: once you have decrypted a message - you cannot decrypt (or encrypt) another message with the same - object. - - You cannot reuse an object for encrypting - or decrypting other data with the same key. - - This function does not remove any padding from the plaintext. - - :Parameters: - ciphertext : bytes/bytearray/memoryview - The piece of data to decrypt. - It can be of any length. - mac_tag : bytes/bytearray/memoryview - This is the *binary* MAC, as received from the sender. - :Keywords: - output : bytearray/memoryview - The location where the plaintext must be written to. - If ``None``, the plaintext is returned. - :Return: the plaintext as ``bytes`` or ``None`` when the ``output`` - parameter specified a location for the result. - :Raises ValueError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - if "decrypt" not in self._next: - raise TypeError("decrypt() can only be called" - " after initialization or an update()") - self._next = ["verify"] - - # Take the MAC and start the cipher for decryption - self._cipher = self._create_ctr_cipher(mac_tag) - - plaintext = self._cipher.decrypt(ciphertext, output=output) - - if hasattr(self, 'nonce'): - self._kdf.update(self.nonce) - self._kdf.update(plaintext if output is None else output) - self.verify(mac_tag) - - return plaintext - - -def _create_siv_cipher(factory, **kwargs): - """Create a new block cipher, configured in - Synthetic Initializaton Vector (SIV) mode. - - :Parameters: - - factory : object - A symmetric cipher module from `Cryptodome.Cipher` - (like `Cryptodome.Cipher.AES`). - - :Keywords: - - key : bytes/bytearray/memoryview - The secret key to use in the symmetric cipher. - It must be 32, 48 or 64 bytes long. - If AES is the chosen cipher, the variants *AES-128*, - *AES-192* and or *AES-256* will be used internally. - - nonce : bytes/bytearray/memoryview - For deterministic encryption, it is not present. - - Otherwise, it is a value that must never be reused - for encrypting message under this key. - - There are no restrictions on its length, - but it is recommended to use at least 16 bytes. - """ - - try: - key = kwargs.pop("key") - except KeyError as e: - raise TypeError("Missing parameter: " + str(e)) - - nonce = kwargs.pop("nonce", None) - - return SivMode(factory, key, nonce, kwargs) diff --git a/resources/lib/deps/Cryptodome/Cipher/_mode_siv.pyi b/resources/lib/deps/Cryptodome/Cipher/_mode_siv.pyi deleted file mode 100644 index 2934f23..0000000 --- a/resources/lib/deps/Cryptodome/Cipher/_mode_siv.pyi +++ /dev/null @@ -1,38 +0,0 @@ -from types import ModuleType -from typing import Union, Tuple, Dict, Optional, overload - -Buffer = Union[bytes, bytearray, memoryview] - -__all__ = ['SivMode'] - -class SivMode(object): - block_size: int - nonce: bytes - - def __init__(self, - factory: ModuleType, - key: Buffer, - nonce: Buffer, - kwargs: Dict) -> None: ... - - def update(self, component: Buffer) -> SivMode: ... - - def encrypt(self, plaintext: Buffer) -> bytes: ... - def decrypt(self, plaintext: Buffer) -> bytes: ... - - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def verify(self, received_mac_tag: Buffer) -> None: ... - def hexverify(self, hex_mac_tag: str) -> None: ... - - @overload - def encrypt_and_digest(self, - plaintext: Buffer) -> Tuple[bytes, bytes]: ... - @overload - def encrypt_and_digest(self, - plaintext: Buffer, - output: Buffer) -> Tuple[None, bytes]: ... - def decrypt_and_verify(self, - ciphertext: Buffer, - received_mac_tag: Buffer, - output: Optional[Union[bytearray, memoryview]] = ...) -> bytes: ... diff --git a/resources/lib/deps/Cryptodome/Cipher/_pkcs1_decode.abi3.so b/resources/lib/deps/Cryptodome/Cipher/_pkcs1_decode.abi3.so deleted file mode 100755 index 71cd3116580ef267b22ace3deb4c26b3b28e496a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56536 zcmeHw3t&{$we~)loMA#B6Cgl%C=(0_YC;lTK|vEhoM2EuP^gbFCKHGaNl1cXe@iG2 zYien+wYIlf$3Fhn+uFx%z1C}6Z$QOIeMEd#ZYwG(h!qrhsMP%5+Gl@fCTAu{>uu}( zZw~CU)?Rz9wbx#I?Z=eoa!`d2>74TxtEMwY|I4D^JUZu(n@-J2JGi^+qZca< ziUm^M7fIx_eLqlZ1LWkCK5YN=Jeg zNu*br%Ht!i$KL{y}%p{`X$Y5`QF zV$r-vO|&_>tgfXs+PrAqtop`==%T7C>Z49s>XJzH>MBuCRbThHsA~FbbxSd{R5#W{ zRdrQ;ePgv+5nWN;bd6f!q*5%F7s+z)<#=;`WR|_^p zol{vcb5^9Juy~S-mK07#qa-NP^x0={3lZl0OH)5VDdYR58*fI~`P9D~+N$Aoa9Wz` zKwb{c101*Wjh~en=cH#FdcZ?xe&_j)K8bNNWHe!S0$tB{4x8GaKxZ9NbR>ZeL){~H zzJvAwkQn6=N}$VF<1sFQ&N)KLiW2CF_LL^j^*A+($`a^OH;-@vJ<(2<_rhKe^m?Gz z1HB&T^+2x&dOgtVflsFg!t37(gm19_c$QM(jUBD_?sH*hc>M!``yG3`CclEBt}(lC z4vs1VCi&%3xMyb%%}=-L?^wHCbUegnaOjpm=+ zEMWXc;r9LEEpML{-g3kjwzh?z`lvMr3J&6OUK9kncGd<*&5p~joiPn^YSo0r;q^0~ zlZ*av`)jTJp!UV-O6|TBChcC1(>8km_^eA=FX5j5gTmUv;HV8^mD*f>QTc`Ci^~^P zv_H1^0yukIcw<-arjB_VAE|6l&kx?TCF9}n#_bMV|50Y}<}HDTgBxFfzbe`fRZe?7 zxb89F;Z1W6hR*>1y8Cp=U;a}ZUeW%1W&7`Uw|@jTwLggF1UJM`T-m z?y4NWGrZ|q8;zKqpS!yaoW+k+ZtCwpQFPAetHIKuLLSHwj&qL5JIk#0T!yr*bY80j3c(Q6uBaBgflwZw;tX; zIwv?fJuAF^iVbsAVoJw(YH`keCX`rM~b)4+%~)D5L*8b+c!ISch1n+?VX(+2ZqcMSb881ps+W2a&$!vKwG%w?6#}VTkEWBlBDcFm^QJK*> zul-R-gb#OC9Nu1$x4kmsx$yd}Rz=2^ifPXTzbNtKH0rjaz@!cLfP~n;uy1(%M+bu& zPD6KBw!afDek$DlaJc=+@Rogn@Rr@#8AqH9ErQ z)ALnu<8}nu?Dh|4w;wr17)XdnC{(m>ae5hUdO1*$0aK;Vb6pH^U}%T~gB7`0DKI#s zxMOzvBeUC|Jp7b1E)kE5Y=vIKIq%W(ybn6J%+Azoh)q^2^F2_g8G1(-j`y8J?J) zU(x=6w(`wD#g@0S%QK#Fa=9)Ra<05hEo}Xg?>aN|R z`+WTqUH8hx^PUjsroHZb@eJmRFT8a7BZ;r46uvoU*w+bi>%0$YO{yKozA zUi*XdWau9N9^N!NBP~2_fAEX1NpAhy*1Yy7!sDN)oc08I6Qs)aC-l^@d(j77U6t*J zbS5mmn$?Z);O>c%zoB2g%p9x3?e}BG3f{Ceyz$SdU#gxZ6>QuQ+;BROdF_WQPrWw3 z36Y9i#ioc=*)F0l?H)-r8)5ZB!43UH4BgdK+5SPrrdC_+e)D}q`oV|+>*ckev{^Vff*QXEA^Ut*BSN&W%BRhDvDsO*i-o``04GMRo^axphBrAB` zeOE;gX6EwFRGm#Y?Uq23%2OF{88~{DtLg~JT zYC(BeP8~R1{wPk%x7G$%KGg@O&OXTQxL;1cuMIA9*^YM92KPQ(8*Kao(iWumMfOD_ zd-Q#XKJChbkxF(z$qNwu5F#%jN#(9sf{0V4;RaXmDAHa?Ha?AXKhgn_+|CLQpu+8@ z!r2yBA#F>Pbfst^B!nAW!J|lf1%DH1t4O_R>Rb7&+c?M4y^z|i zxq?UGyuGMrujC&@dJXuqA}uPGzk=MOj%R}_pV97p6?CWxE_=Y?J_?C_e+D16K8-?q z#+BYqNi-HhPg4?gf-86wX|L1|seQWurHjxRjqe11BBH0p}CkRC(kN+6BWGEi|sJt#Um*!Z$Aekj;} zkd6xHuHg97@&(bk_aL`NM9|QdgX3lg$G7HZ3qJ~4-jn>b`5~0I92ytg_SW^_ego}3 z6wsxh??4c5d;NN;<{(Uc3rL;ld<|uFuj5qpwrD#n449BLi`E7n_#ah>^22i8vDZ~K zDPO3E#+@D9c3A5^Dwp0UrtU8&dkY%s_Bpj^^PvN!5`<1wTCG&&%1_sZ!-~W< zYi748e-VY^6sIW%-KOZ8w71>?%Z|M`H6O}FEQ&9i-+}M;*5qFv9FG%hKtML{2d()% zuxL}^AK3f0;~z9Z`a`Tmz%(BOQ}cmrFla9#8lV#5#!pfYEY`j~C`|httI+C1b9I5~ zNNu6m=7e0;7KqjDk;)}F5v~YeT{(nakTMBOaDkx`5e0x@hR}wd`$gbEU}y$hS_-V? zy&_?MLHN?er#oZW_+TachcbWX)uObP(qVP0qXl7y`BHXedVrhN{^}P@XuYVb> z+xtM}ru;y~rs)|K7J{Jh;mS>y^jBc`Lplm!I^qMFw8V_%WY%n}z}{=a_pmU}Yu{Zl z{;`VjzYmXpvSK_Wd~{Cc==7j-y_KOY{9+3>G}!28?8io+vc0pief#Y8_rqJ>#2#vQ zAVc!VgZGo~q+Pzt@ zFFYVyVC=6GcJR_gO|v)Ez zJhW#O_S&*H#(j_YZ4TKt^&Y(*==DIa2YNlw>w#Vm^m?Gz1HB&T^+2x&dOgtVf&YI! zkodByTrp_jY5mIKazt*h%UKsn@HaXGBd zEmYR(Y->cnK7q}6swuMa?)JAJyB1JoWzWgVog2)&IOVlrR zc6I4@pl4=fe>H7pR_<4PGqXaQ(r0Fk`?5VdtLQp^c~w#Vm^m?Gz1HB&T z^+2x&dOgtVfnE>vdf?OS0e_Fx}yhI(>hPT z?l<|nOgUaU(B-CiruKgv;P>p_8mDAClsN`oXke{@R~dMnfj1j?&a7FdhsNQH@bRJI zqC$MS8!9O(DK09WSQHw!Fj^A|SGAggg5u)w>NM2ibcquH{LK00pw}QLAH`?5j^_f_ z58=~({H{KgU|LADKQ(k3fG^NzssJl64`RNwK073r-Ul!BgXlkB6YYxu($np_hE@pJ zEhw`CD7{ch1GaoK4m~9R{=ipo?vpkG#`}tL%a93t6{n1tXgD)#CGZZUzJm0vmQw*v zDpTUce#+7e&%5M)mkRk6tN)uq5*d||DVk2vjD3MRny!~}M^h%Mwx4!1jTHtVJ7xW^ zMcx`~WlP20FUqZ3Z$MCIMOO&Nse*G11uq1lBCaUF3Ho1)FfPPIVZYU~vsn_C+{Z1y+WbcNq9VA^uwGDYW`Y zh=nfUJ4rN7u=-0MTngg(NZI>DxX8-75p5ic ziQ`11?6-wj>=3^O!gWGCEW{Frm=EqDNCVFj(=V~C>q02SG3d`(O3jfE>~W;2+fjuT zwt`aub8--`4?$=PQchoFMj)PtBISGpp^JcAfHYWShJ;W&z%4xQo&ckV_=)c71 zN8Ol#tnUQ1>KKG?5pAOS4?v!kjHn)rfEs`__;Rr4is}e*OObM=ik|`bv1FvG_kp}4 z8L6@gy?zl=u2lUT5P3a(m=ELY=f;FMw%lp6vhEu3CwMz22V6OrY5O7No`}pHKsHEb z95VU+aSISBcM>vfKvp6REk}l$1FBORH?@DxLQqdbGe#nz%WF*49mpI)O(U$uX&8mW zuQT*Z!9EN=9APc$^LcgSFWUU0M_3Ee+MwXJ z34LxYvFuD(k@E_QooMq{@3utFd4r@~jK26E;elq=Oy6FZBWU{dK_eIL7N2U@;7-_I*%e@1XFC(`ODfgGiSSMhhOXj!8oQ6z3 z(opfh6v2K-lEhOvB_S0m! z`D{KWDWp94s>#|uwE(hR$R7||OcXt>`Mx?vsV^esjS{}6X9(XB{yxZcOm}*(&(nf7G*uJ7nP=ym8wTNX_)Z6px25YBmYCB{DHE7ypZ!% z=sW3KqWcee+1QP;og$-`tQYl)RXGliB9O*J8)}p_+_Ij-pp|3n0fS&Z(#N+DLfOv% zwX&>iG4&S^`x(;7S7Xi~H-YCZQqBfs3V@u7G+6j^ z?*L^Pa*L62e~HYDQ&5bg=c3tWmVgiRVMT`OfpP7rO0^<6V*Al}LIL@D(fd zp=4xwxCxmrBk74_6t-3;An6(5Tp+WMbVKxva4&NAAn6%lCy+l$MrMSJiI~=r#>#80>!*dD$LJo&n8B+Qq**Q>mXKX&3+IEM$WW5ie!YEI9pzZl@$ry`8!i}2Qq`<79K5`!&!7P>tr47OXsSmBXQosLXS5l$V*-G`KukIZX8UX@HPGJPv>0fUsg z2$^Ca1(LZ8nFb)sC37V*zX9@VBpnDe10QA@gBoeZl|%eug$@L;za#Pz2p`Oaht*O*jA(@q$28>0Or^<%XQNnUd z&6o1V0Zy43^avvQ2_uXv7b(oO2Hhyq@1iK@b)1Hr2Sn0~$eaOW8q#3l&HV_J%aOYb z39%SAkJgj^&!4}pv6oYVztfGb0z@~l`MTPaz2H@qwmao^Tm|pMl&yg64X)sO{Ep?=$mg|j5iCO}Jtqov>usb+%XIT;9rD4Q@=S|zm>YySLiN>wEZ zexK-gt>APiIN9lC2}_Ya`2>+xC34F_ehbnMFu)p+&TzzB%5*Vul&nUFc8b%c-533k7eV7Gu zUt5=Ex~2BC%y?z;!Eo+m6m(s%v|i+QJ|y%E0D;>#jshY zkW!;uC!xp0q1~JmGEUOHX)MVzPD-SWHV#Roon#!BNXt*8VRHAE$IwY^CPrtX zC$J(WYH;LB3e{P|rVne#a#SPAuO5y;q3#7{Ss!Ng$65cbs*I_Y=4v&ev8i=J^YRHb z(W_b;8|(4QhXut&6PgR~0z!WCF*s)zaEr*RYIu4Qg}n=~t|( zt8Xo+Yru{Shi$2hG+x2^p0zC79)u$&XYgR#^81=CJ1a2U9$9N!zSYz1kqhmd>Gs${ zJ5Yb>)wZqD(k*}BEPIhgEc*n3bM4WOFR^WZtv%Xr2{OYD_@UPSb-O^|bi1GL*0$Q| zc7bn~vh0ySz_rAGv7PPvTAO7DhlAJmDvC1vzT4UY&)GvD7WDhR+jg66=fJi>b`XVw zz~2OYIrb<&Dg`sB1HhW&KP{P|@P9l5TIRGdCmYQhDh?Rs_pPuB?E%vAoDw_Fe}-vz z-T;G)fmHT$_L%8*_Vk2uO`hu|w0;lniuryjy&Nw+9C-8d;Bj=G1*|3n5j;h14k#!f^0B!&qh z{C^)-B4^pS%kJ;%Tq6_xAj}HBvo)4-5s-;+x;@Iba(ee+ z{J&s1kS=LSSPC-ynC`g<{B20W($T6XhE(S~e^d`vS)wz21y;cs=)4(rCbG7Ff)0#j zwyYGRe7|iA3@pUZk6meJCc}>@z;_Rpek|R3_IgMLyzkef4DtK!2W2P#TtP*;iWYw&4CiX@9s7$VW(irMkYfqld^cq+W9FU<$lsG zayr%?;+rA9uw-Lp)!KHkzt+D5ykHwMR&9hCNbhS!$20KyJOAg zqkJ3IJcqdQeId>Azk6dK&%eaZ3~aFN)3CP4e2;+j-KeimeTa+xlJ)KS{B(!nyEP4g zCp5qBzd)4hT3?Qw2Dv9E>u?!;m{he4rZNqEO;~*d)4Zrg*Qzf|hdB%@ae*qBY*QR2 zY)ypVYSfza?(#SlwSMXpE&1=V=o2dv7cP<0bh;gv$&!~F1OkuS?Yh^{qko&UmcqV6 zS1Bi4ybHhuya2g~7dSU=WuB&EV=d6NQd6-^e;SB7(1&O>1Q(g4>3D=jOm911F;{uwe4N3Hn@a+4O>3<&QBoJ``;=VYp<3mwiD%-^wLWnsBL{B$9Rt)UNxh)y6BByY3U~ z7aL$NtoNUa!uT`sqMXm6m^9(8X|VlqSN*(rWQ=@V`O_5CRHhjyIUG1nfG_C0He zoh_s>bM>vLw6O#2?0a=Cfb!dPCha5;m)K*lS*g`D2gjW&lyA1j1m3l?as2@eyEG~A zFf@-#ltB?B@6=TU8~|mwrQw`)1<;UI3L(G%E=AsDeVY?%1&&(R+1Y{VPE7#XWLShc zAU6gqx7i`wy-J%V+t{D9p<)Q!rWqVI;HY;A?zN;9>y35tEwZ(C_6wly#QNkR%?&*9l!=guERd&X4XY|Thx0q z>+f|l%kR4d%VCz^ckuJLEtBWFAq^ngx2#a2U}Ww3^|$NG^AfC!c(M|hD-bh=@8Z^4 z&9bp=E_b+o-#csO-s`&ncUbPU`xT`1!Gop=P0fv0Myp%-r?y(EYNL^sXgx@gx|-Fh z1%II{(yCgbRn67QBl4Ucf8lFcNS{_UwA3wYz^nQ4)_b?V;uT$?S@qxTO3;`fDZaGAUHBEL3p*#85%))y-8+R{_qin5C+nKepy{ zeq*y*1=CB>_g6Qg$0O1qcn+wlmp9f`M-`q+PDt!>JVP`cE%g}t^@Bm@RFv+(ZyIQ+ z#YA`Gr2#p7>c-AB%*eaaa-^#R*5Vj*b%qLQqWs=NOFJ^&GwWXK*1d@6b%x|qtyYW~ zs+77H#k|_O=c#+Ht9t>_@upptx`Zg(A(|Jsty<=`>Jp-Cox{l7x;P`zL|Yg1Xe+I} z#kF!FQJMt*{i2N-R%oe(L|KvM>+XZZTyjlISkw0Y4x^E{-M>PbT>DbiUgrAlGNOwONorpjV~#di>N29V!ct3##tphG$slX2QOt@E0bVBg zi-WJ(4)uKlEpI8vSv@$QUVix=|P*8Zwo{nZ0d~ zm$1Dw$WjrabU~ciHOnB5rRNw6&NWclMEp_Lx&>afmRjJ|CwaWZVh%RvKU^L2iT*oY zEs_J)4mXV*Y2X~yqyk;T14a1`EX~i;NZ7zY$RI2zW5GCs3y&)>r zh*lt&R;@`|w_t9W)~?I6dchq1)XUEL@6_K0``P86`8(Jz`jfje;`B-UY+_b@#vpeX zC;>wJPD8`Hm_&~n5@w=~HQphbYQX5>WkwC_x+kG7;=hg8MKnHwFQmg*qopn+%0VKP z6X)mvTfD^%=Ekee(xOKaEF%7F+#;e!T+#(ZX(SwSPn*|FO*|e$HKW&5d4p0}A3w`* zD#L)I12~m|l%zF5CneKVvFkgE3hj7g1Im2Tm82nF;?*PVVkFUqeoh1hk*grszR96hQ~;G#FX@LN`@!( zj;O&%vyNpMl~`)g(?;Je13@|-WAzY76CWI_9{EyG4s2qGvOdfvMB`PvTb+3LB`?0B z5O0a{cT(etDZPegpy;8J3lv9;e`u9&K7|)JTL7U+U6|W5h3YX(ch@RW5B} zjCj_)9?g5Sxh;%CIDUgUeSEBmOm)HZey0 zR+ly@M*N2^ZE}qGT`p}(j5tS-*i4)-O_e4M7rRd%Hy+qt#3oVpZf?rXZ^A0;?d%L( zM#&h|hmU&bf>bU-bu6Y=0@%uwVtbV5`iXwPo{T7)j7>UGwiH}M2~i8JA`IYyFX5lf z8027p2t)89<7y~o1YR5`F#scR663ErPGa~ii<20ApN*5~F~Ss^P+u;0m)u(Rya7(; z#U7syAWUZRt9OTDc1t?a`}FDCw}1ca>_LNu3>h}evRqSWlJqcfLKy_a3DZcI#nIueK zDn75I88kgk!WUus2*gS9V7x$t0131$C6oKW^J z#MyMQnK+@?Oq@_`CQc|e6DJg#i4%&=#0kY_;)G%|al%x4F1hefir#07FeHhxf8`#) z)G;!DlR~v89V?cm<~u@(@gTUqKJl{>s!HWSPW0p<^iK5N8dRFkPW;C)N{BWxzzEH- zWsDID8N+s!iEu(8W7sm%2!)Jc%fKTPGKMW?T{ zcTIXgA-6&)$>(jjO_?m2bmua)E}b< z7E92D%>L|lcdxsU=no9Zr<&Vh%=|RY=dD}Rc^i$k7=c?|!KCY8tS$Qb^H}y#4Z(uCzOSPIH4>Q#0kY_;)Jre5hs+z zjX0q!Zo~=2X5xghxDh9m#f|t{^Wa!)CQc|eGomM$AQYR4PjqR;G2&BQT1kxf87^&N zjQCkDZBmT5{6>o7`^hol94lfoaYC_~IHA}~{5)4*QH(gD_?dXr`6<01qzhe3l%O6p88@PxPq82BF<7e-!SavDb z1&DAw6}xpU;BcB8Cvh}Qi<3B*X2eMxOXYDAhtk<`5QW8Ybh0%_Jou&sUJ)X? z!Q2ste~5nFkmS{m#WCgvy3bZGB|6fO97g8cU#L6w>nOCV)*x8EiP8}4*oe{!scKOQ zn=VUb)1p|iO-UO*+CfMo;8LP(u89$%n_bc(qHMRA*z?;)tVXFkSH(i2vrLEKofx8Q zmuOuWV~*_tBhkd_7bY#+@oF86O~VrkqSu;k$HwgTHf9q2zQpxA>(Z=V)yO9W9_zqM zLPXiKxam(cv9hIZWo$DTiL%Y&l%**;Iq5Ni*jQ$s8f-BT9n1=U;0}VM*J`9ky7@#|vF<=t&9?Xygk6;eNmFE_v-;o~H?&5LPGa@BtZYomIEl0U4I1$jXo=au zb+a@eH>d&uZb}h|H2XH=1zB8(6Ut_SIH7FXh!e`DjX0rf+K3a%rj0nEY=DUGa&4I! zBTiqX+D!D3htlzTLum2wvVM&}&!jd?l>~V<` zrV2}VQ{O71H{XtXlPH^sSR_h!^}O*V2kX8W8{gzh(|;~S0xqeSShY%cXeu z2ePGa|aneTy z#e4qxfUY!yrpHMPF+y-T?C~?ksqDzC8x83W1My>Ws>s1(q!ISV4Jo&Dv|B7cAuES2 zwv6!T7|Glzd88eg_}NUusSFz8HE48VjVv7>oC>tx{hg`mr z81cWjw23j|eN6bt#)`Q72B#ddu_8XorA>(uXOxJ|#0gVvtehv1^gdgJ7$(Yy?0FAG z4mR`qpY{OlaYN;}7+Whgo=uWyBu)q)5oKq_ZxfQj?!!_^ls$#_=7_R`z(tfW#Z>0K zO~}sUtOJn_(~_+QLNN!1kNEu~H`Zy)2zmK2^gsII+dV=nF&vILnN_?c-ss%)8l25H{ zi!s9{ryeZoa^pDpTU~NsV0!~Q#qW zn@gJ%BYw9_n;av~evussaY9+$i4)4|PW*mXUr~%Wq3lSAzv%Ln#E8G_(k8};?{#UD zV#E)+w8=5zJ`?z2GjT?T*i4+T=N$>X&lX{|B+5vXn}Er2C9jo}W1BJYmQQP7im`fV zC!D%J#AKt{V-khL2{8!}WhcgWBBZd};7FqEAxm9MlpO;uqJ$}?8t+boo%U_xcWB{= ziMgct956qKlQ>@fHBRDi`B|LA(ekS}iG$^yIEfx3OkuZvx3t@>`?)4?=eq%16eCV3 z1BW=F+#@AUDECN-6UtVeIH7Fii4)4eA^ug@7Q6sya6T7GHE?i&?zwfZ)bX?nALg-2 z!T>HUOUKCf)ua&0%?~NT&HGfv0~3pJyFC?8ZW52&l265XVh@~h^FEQE-pM~EzudeR zDRO_q_!I#`l+y<- z{?sKcB>Hnh8jMCIPjfLA|65!#VG_}3@}mIirjX1g#0e$Li4#hg6DO1~Cr&6~PMlD} zoH(I`IdMW+ABYpC3UlX^4*HTULMIZXXC%&&9VZumGA5e+v5T{ zi!MkN34B~Y9zyTq0@4Y4dR)MA!jF4gKt96beq2BS!oU7;0VN4P!Q%p|ApA!>F5u|k z^pY^eeKS5^{EI4$7&Cvm#5hEPTa@wlZ)H<_hApDOhTqurEU>b7$6B?gVF zOYy0CAJ>u#T*-yWl1YBm>y7Ub(-*J0eX@Y)YlhUrZDlY2%#P zFKJ)rV|WotN4{y=PMDfjoPw_;1#Myq+N2b;$th@4TpArMiy(2rREwZQ+V4#}9yidB zPFji8jrk`HJvU|N$&bT`mTrf42kE1a2vhkAlM9OpaD>IPF~gBjd}=tplmR0^h~ zFJR79)WCPbQh4NAqq@yNKG-Atg`uBiF66wHcyDrd7>2C|dT(!g?`?Z;Y6Wm02;m?h{*FH}8518_Y4CI>V z9h2Sx=^c&Uq39il-a*Kb=Y?EZz4k6JgOTez7kNT2|AmI1I|}jFh_r#g+GvM;79}W41CSN73O1R{z%bPCeM$+y!zk!2kAd*>LL7lwa5E2 zG2Y*Z@%})J_t#;(KMfN#e&z4Ncz+azzXaoj{B0huzYiFF?;FTZGk;^gY`D)r{>T9# ze^1b>&->XtfAWLh%%_?D=LhrNujRd;%6q?)_kJYLFXO$CzeeG;?*jdPwo(@v$Zv}I zy{?y^-{E>;m67L1xn6jsDX;GVwir4;)Ahp7nR0%w>xEx1<@|8h3%#Gx^GjZSq3ebG zT-w`ye&)$fNf{r69LG1Ce)Y<~Xzbi?AU`r3Vm^`^X&^t4^}fG6Po{xXM6&eAN?t z+VIgo{MO0)p_BJ3C+{at-tU{dA2)fwY~trkUdS)uy!~^`ybq6NOH*t_e0W{w@|1L+ z`c_JMy4r0PxWo#5cz5lt6dOe!{?K!0N;-PiOnlw-1k@cV>3Haroyh6nKGj!+QqnWk z?J4Q~)R9msJs1r|spwg%EG0dtmZzj=t0VJL=^3EZd8z0FRUjpOkjhR;&rv(iPo-zD zN^x<7%e*0K{`^$@_~X$j4qw90<|Y>V)Ud>?i~H1Y7f(cdYD8kz#eHg|izgyJbwXm+ z#eM2T7bhauE9vT;F6IgQ0ZCKI{VwaM&^xoq>A*Zg&qmiI9#fIA68vVakJKzpPwq$g zrb+bA_vpDw^C!o{H$YEie^2fDfz}gLvx|Xvj#PSn2_8!&$JKvodU9MntaT>G`3}uL zI4OSKFm!Lc$;*KQwXW``AQ~3xPSUwkK_3XeZAR$IQHwuXF2__&PadZ;3_t&N^eJXu znXBoJY~uN{9_Y=WhY}eL{)Ha+Z|Z@5M-O_of<8Kh{y%9wDdw^2=GKj5thfdu3AqS3Tf4D(ezHFjnX|Bb-r13} z&zl{INOdiZk>ynlHT6*ynZ5M9@_G2P0Fem(`atCDuwf0)UI@~nd9x^VPUZPC%PS-2 z&zW<<*^45J%4b%d9f3*FXj$1gl@&8*MM?@w3MZ>b#iDtUxF4WEys)V13e=VOhY)o0 zy8kr^=Z_$CSLgh(g6_0PWZ8;F{JjSJafenk$N75>ahBpq=8s&&Nv>h+o^EDEBGs#_ z#Ga~p{1pt3l(<%@!j@}Rv{qdK*xKyC<-{5qTcd@`8dep`-^DL^Te-(Uf9r{VgO}p=6KAZ4UQGg!vw&-fc`F z$`5sQHB$a^gAh(K^4u<&eWa_k6qrnTl;?hna6aY>Iiy}eDW5Au9A!wNoATV35%Pv2 zSoC4kF{{GBx)SW?{*90;HSK3Tgz_}#qXc>G^9Z>;^4d=WYJf-?^||{&v;VupFtUDc z`x}h>OvA^0qYD${Wr^|1uLezgVy=(k`%AM=@a}BH*2JR)nM8T+LkZU=G6U!KgK&*U zp8Hk83(08oc(Q7PN1ppyLaa314zK-RGV-qfiwwJgW!F zpDS}j=DU&(LOH_MK^C0$^EzXGS$BRX=hQ`9;9exDpJ697IVeBD$+~CZm4ooT1Y8%G zQ(KvXc=b|_ygm@TG%5RenZr@%+0T1PJ&X-YmA=m z^-$^0L&mh2pI&;h)<@}Q3!}c9Kf{fD=BG9$;<|j7J4F0#hwB+`{){!snV)94u9TC0 z-mNpIy_?bAo#rLL=)d`C?gD8yh?kXDht55fvX+;B9kVAT@~M!R!Z-I%OnI|f{dLk8 zN%!O><+T1yS;+z>dK1**%kVkrX!_fiUTARU?Rgjde>!sBU()X6a!Dw6o5WD!!fq2k zkGR|Am;T%?e~UZBcO$+AubbUOBFZT~%AuxuibDpB8^lZE*HzMjN>%+s+g9Z`J?8#0 zqLuPmeqSXh#*8sO)6<8I&Ki@RlRGFoH$7cRA2DV`u97~K4<$XRN2l~5nc11cM&#sX zX7}jSepJ?&%pQXVkIK~3Zl98#F=3EYFlf|>Ntud0zB`p?jZM!Qo0&Z*cSP10WoUM$ z883|%Cu9s7H7YAZ88u??*xcdSnS+Kj%E@XJDd7z9HB<OlsFYy>X+)jbu7x>*(9Z zjrA}7?|I_Q{g+p11e4*WtTBdbo=fu_g_YG4E*t07j6Z2sV4g>#ykdlpJ*-E_o-?+v z7ICsfym@vcp|C`}*@nblC=qXubNp(uZm+Vhu ziTINBYlZ%M`)>yRn}Ppk;J+F8ZwCIGf&XUUzZv*%2L9y?Bu+o;m6)#{%~F)anOkzz z!d6t2IDLoLcD=%)rl&|Ms(G4sTeY_MB&R~~gQ%dUQGr*{$)UDtj^grD;<}Qgc~$Du-o=HoT&khe0z}Hw%g(C5Jd78 zl5I2kBl?LC(c?q%lS1R8w%GPKQ$xwZGZR_7c^>KKaBdl3@4tOv;ygcRJBrvgY@d3G z8Z5RA=M(bXr`o5UREmg?+Gckivu`a7Nw95jpXv$*#M?G(G4rvWBslWjUzSo7)3RNN zI^;|X&??K6Q2or;UTo3(oNk67qeW_Nm{< zSmL90+npEfThGW?e4TpGw&7QY4DgJMXpfnjHc58m`>vO=6poMD>L9|I8fl+;k@mct zJEOKcJ2auO^jPQk7Pbw)F#h$2wQcx4zsdTk-|GXqVB7GA z5tIR4kO|ZV)K(u*fZh4Avx9^2*rPsorX|{K8!pm(M~8E_UQC;|3l8T&vqkEVz-=0m z5xUx9=IGJ*E_NqN(u1jt+CKGn`V_`w_LilQb#bQmH%36$iU;)%hcZwfN?UCxL(HKJ z*M}mLIqH99C}XGYr2kVGkSs~AY@qbXjAnv4ntwkO=cmr&=2n|Mg;w+tRgSvkoGk~K z7WO)q%*E&2IEQz0|Cqb&>poap2BsKd@n4#CG!JMC?=GpSjT&a@T|=iYdUJ{<3a zQE9Qxw6<*1L(&|zYAtfw*;Ed_U5EE^a+0hA&fRR#Xv#rT&QBNwJ#*xTE^;_e#XI+F zi9*NY+Q`qz*c0thzdF-6*7Sv*oucs`GGtCD16ai6;BXG>ZD(4YY5nPk4Dnli0r62s>`_OwIX&ixI$@9c!tUJW>@dU$cG%yCoJZoL zzI0{{baohS+ptZ>>^@Z{*_oD0>tkz5dk$y%DDPv=BVvdPVBE~GOt3byQO9f>zA{2Z zO4PBa4;{`!QH4?G>``Z&SreQcIF2;D7k(Tuj*E7DN;kG~3ROFt3*;baO-geNkj5C0 zv>RqLs;#QoG~K^MgHea#jTM#4X003@{iLw84i+`)Fyk^$&`MHeePxi;?QkxUL*&r6 zy}rWU$0=p5FYqogT1l~O__}z&$GLTWjQTW+!O8itM_qIl$hje(wyOJR;7tk3%~$X) zK2~We7prsQI?2!V%D6X4yxg*`y)Aby4Ux_YZzFjME393+`M#Whlf)F6_Y-H?m#`!1 z~K2j7w7mwXZqPrF-L4OBhX&jM-|NR>+DZ&g&4Cw%hhQqKAdc1n18dTeNM%Pj=fuuI*U~(e8`lou{BhfFrs?q$3(hd z8n55C{*1!;BEP|!cF_&qj(2_}SDbz8`GDxG7F>Vx$x@UM?Yo{H$Kt<0uFXl#T?q8* zjLQ-2`y?@&9L|%B)Fv@3Y~qK$t9yPg7h{rg4B-0FoHvjpc65ScAC_}@-PHh z7bcrJK9(hs9_p*Yigb~cA?2{K6vi6Ja@?{GJxZ^`t&1R7bS(laqOSp0g*J$y;(33W z`?`3}pCTlK(>kUtQ-6ANK^BIkkP+xBz>1KC1u@n^79l!)AbIb09fpwi4(o8oH5hJo z|F-l0H77`$`{>kB%)hKAYp{n{n%bg|XqvGO#u~_SNWv|rV=P7PW*u@%ufsn%4-;-W z50h!K#Ca%7ze}HoqS(7Q4+T>cxibFAk$6e8>YZ2PuTF&~Yyb9OBuK}j7tE!Z;*KLRlyK>lxyuu>R@8IJT_&(9y)YB7SumRAWGp^68Cs<8lWZpAoQR&# zV8tOJ*5p;eO09u^v_7J?@4P-Yofhf59CmG6>y)BwQU2Q1)Z&QVC=#TYS+8=XYikm9 zp!mojh#Cls-1jc;`2?3M6NXUZLb32=)lELNI5c^vLK9 zv&j{7Nxy;?i0C0V=ggO@=aSHc;57lBakmH}xPlU*gWk3~&l?zXL@yCB#7#r)BAs&U z$zovm7~g3}uYFe=W{_MZ(G6r-K*9T@IDe80V*2Fp(`9V)+gs&CeJC=8s(3;^q+MS- z^g6j;_G?^QyG8FPXluX~vtMR{4$2i{-Y@Y^9^K@Ac}Zjym1$@r*0^H+EOEt`DtpS0ExyKT5k9*Z0J+zw~RSP{t<JOmmvaP_Ai1F_$S<;#|v=KPvom zVm{J!>vwL689#TN7rqK%gr@7%hy}@y6?jdFBsw>ZlR@LMKLS?amYTv ze(&^?R(rZ>_wJ`XP?07nTC`{qMfv;ZtNZ+TRFdbbLEVce&E!Qe|&!mnSgL%k@Q}3;G?) z!%HFe!IE&w(3DmrI!KqUL2^ZMn`nF&xv5=|+QS{sD?dojw99069j!sRdA}f!_QWDu zhMU>AC)ZWgbW8*C5h)j?-Fa3NCzG(4G_9($d{waFxZKR#STFL7#1obyy2&z!b00Gk zNm=?p6U1@JekoU??3Cyx1(Z7`x1%FEe3|nnF2*J$GKWiMXQ6iDqr(eCGBGMzWdF+G z^}B)reJ0{TMigDpg7V~I_VmX{dH!PZV~K{U#-P5aBBSx0Mhqju=%XzbBicrSHX?bp z^_?vXr9JKLGL|cPZCfgm6_WO4g`_QWm5g;UR*8C89fy{`P*z|I3IA|;61CFB%i~&> zirNUw^^n2Rdw0F}vVhtYM=$6|d0DK#TAnUi8)rjUB3X&zWh${muI1@LW^*n7dU?)- zlw2NJn%?Y_&!=+D;oEt$Ii^D_O za_WgLSjv0xs(8|0jb|mF(JqCQI6oJKQyM9GGNB?28bPRdF(Mag4`u4gp!?rB2rqt~t` zld(FkXS7PBlzK*s;~7oNdI&1bL?_Q&+(gk$UUr_~SqzoKMYK-q?AegEn)OaDohD+T zlp7$r$y&-5YJ&>Lka?7==uNUS1&1jtC14=A&-18;B~zXTuL?NmzM-ay4QbjWtlXwpsf>&w1Z5=kds9!l=Eo}fvfleL!@<{qHiM+c2k-m$`r_0 z^nF?45Y6Yu#Cg_~j_3k`F-(#;m-3hAXdcj(k>zjK=#bXeyI!MDq|Sf8M#m^#YK^`m zqIiwo2)@G_JxRh{uhB{B{M$7;O{RapMun9At2O#DNY?1Abi}nrpGur-jc)UFCWnb< zgEhWDQhzkt!Z``opHY?)8}HN}&x{8%o-4E`vd?((a-NU}6Lyt6lgZBEam{!%)1TGu z?g1^DM$88^tqa=u%MEwGeajzd&wNM!S<`oVM%(D4wXO1uYK$IAxs@^K9I!sz4$t+Fy?qsQIgX=Jkj|fKZ9kP@e7gc#d6a6uK17uGV%@B zbEo+^s6^YClk&EcAGvx(`%3J=u%Wh5I})Ao0k*R7d@Xr3#5QAyvetk@_f0y_y?@CZ~L4x<$E&6yzmo}wWIS~;=HE==rCW) z7d}95iNTxb{JL;7A?MC{?lUDLou9v$%2^(AKPO`I2-pO~>bah`9!Bj3}J@9l8f z98Sx;WJ_Yk9!Z>+C_d8kEUt7+Cq}yXZcVq!m!M`A>-0hi+?*?Bn{Dm{zQ&w>!(yAC zA!C@=i!VX^c5tgEZ9V1AM8A#j?l@0y_BaP_2ktA}8QeZx58MN|#kj_} z7jfHg_u%4i=WrcykK=rB?QmCc+i{(7UbsfMXK+Jtb#R~K!f~(T#^L(mp2a2MM&t5v zALDA`vT>JjzvG7BX5oh8mg36eKEXYY>yO)y8-UBiZNNQ>8-*K-OT=}-UB~^5dmDEg zcP}m&mw-#dy@Z>Gdm1+bw-9#>_bN_SXfEzu+!0(E+#k5pxZXGww+Z(KE*|HBJA`Y1 ztBPBI+ly<1v*FxuTXAb~@8PUC3oa1%2yP^r}Er*MDbn&S#_p}0KU*SImb4!DcBO1S%RRd982 zAL5+2EZj!iV4Oeh65`7TgWo1l$2!U)-;_ zYPb`)Rk&!}9GoX^7cL679#;+*fa`;+jhliyi%Z81#C63zjC&bZ5%(4@8Rv&!hV{G>`P%QJ$IV$ZZvSsJJG!M_`|P_9KigNY))Yt6w-!7%_^YJ8>uV0H zyg7cU|BnN{*>YpMTVBM}au+(zdu~a>$en*4vHp<0Ao%g+kACpeq|uKb@6+v*hST5g zeWZGBQr!_J=R0=q$-B?F?bitt=Pw)Cx_9jp_rLxAhSYB^RCsO9o}sJ59(=Rq!HkgP zufLlXRcXn<=&pm~AMU>XM(E0^e=e@?HMaNg#03?v$1k2e>Z@)WFF)|= zJx6k~Pr0Rho3^0Z(0Z@-eBHCoPZ8E_Z_Rr0fe(+o;MF|x((F3-y#B%mZkzA3{@j0B z`A6!9e)Q6#Z%+K>!X?kn&zu^OdEnRM9!K9-Kdv^o$&N>Eyx8RF?v0*Z+4C2*@i&h= zpZC-6J~3yfw|(XOzY$~Pn@f~V$oAmQa{`>=-IF@`;JH%A6mXrxl?5}Kepw&dMjhw zpR7@?N`=b)K7QVpUfY%5@a_nm?{?`+Z;Tyl2DkYd>$wxOn=rywoFr;uy#_}&7c&F6??+LZW{2J2dtYzbq(^oy2`hDh@$6tBq`=aVzAH942 z`@-POpFj0O^1)_VKYrp}Q1{cx>!Q}a{(QZtd)n-bT>L`QEkmEpZT@cOYcKAdc5>aJ zh0(#sUb>jP)ZPBa@v{&6cR5fY;`c@4=6%|!eW!Kj_xI@Z%IUA&pIGtjTOW6W!R(L4u5ebx>vyHzz&~2Id{-!%_sRj z(`SCr<)56ffd|jketBclJ%hga`Mc0bvFkf5@ALKHo^5h2g-kpcl=DIJUEO~?@JD*T zSFV2@lodB&))$VwQ-4|ecIzF(TQq1AzAe4&bLF$QS9#aB=BYaQNwG!t$NggNx2*E| z>J8sx%TK>KWZ~9c4-A^I@95YKjT3YGk2=&YZPTv+zzzT^0D#&6;8g(dGyu2)0KNqP zkpN&E0N4)z+yKCL0H7WKXbJ!Z1Az4aU^4*t5dho(01*J7BLGMM07n480s!zR02mDb zx&eUq0YEMQI0*pu0Dx@(U_Joo4FKK-0N((BIRGFG02~AWUju+j0H7-X=nepe0DzGI zU@8Fc0sx5sARYjG1ppoZ067341prh706hV~PXOR80PrCIXbu4C0DunwfE56g2LK-d zfQbOW699|=0LKA<3ILh_fENKkBLMIV0C)rd{0;!71Au`5;C=uw1^`qC04D%IYXI;) z0O$b#b^rhm0MG>hECm4X0f4pu;Bx>_2mqV_pdtX+4FDzsfOY_21^|%ze+mGo4*<#n zfW8309{|h*0BZn1LjYg{0Q~_#KLF4S0F(g$X#n6H0C)-jYykjY0)X)Vpd0{r3;?VI z05t$WWdPs}0P+FA1^`eM09*tBc>rJ;0C*SxSO7pa02m4Yt^$De0N@?~@G}574FIA5 zKn4I<0swXbfMfvh4gmNQ0MrElD*(Xj0ALmX*a`sN003hFz+wP!1^`?G02ctjg8(2J z03-o`764!`0Qdv|{00Cf0f3DF;57iy5&%p90O@$V6#zUA0Ez&>y8s{<0Q>*|vH(B<09XeAo(BMJ z0Kf|X;8_6B830TJ01E-YO8~$f0GtH?2LQk#0MH2loCg4}0Dva|z{ddKJ^;`F0AvDy zdjY^)05BT>_yB-m0N@J%5C8x^1puD`fM)=}WdINe0A2;eFP0D$WNAPxXH0Ki%RFdP7c1AylMKotOR3IN0c06ze*3IO;5fHwg^F95I)05k>w zqX57r0Pqq3$OHh-1Au!0z(@cP4*+@t04o5v0suw>fN=nzBLMge0PF(*4gl~R0O$(< zDgyw20I&rBO&0PY6> zsQ}jG+008X( zKt2HI2LR3ifHDBU1^}u806G7&0Km@xU@QR01^`6>palRp3IJjNz~=zqMF7wP05k#s z!vMe!0N@D#um}Kr2mrzWKneh;1OPSzfO-JnBmk%Y0Q>;JE !0K5zUDgppc0B`~T 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 diff --git a/resources/lib/deps/Cryptodome/Cipher/_raw_aesni.abi3.so b/resources/lib/deps/Cryptodome/Cipher/_raw_aesni.abi3.so deleted file mode 100755 index 5f08fe70a46570491e98266c72677761392723d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106384 zcmeEv3w%|@wf^4c+vRY-US1SLE)AZS2Pd^JP}l8O*RKzy(h1=Oh2 zP~Y{j(^{?fYTIZZTHD&7tCDt?>WW?D=M&Gv|QzalNxnDfQ-kvEd5QTFt+ztw z)Du?pGL8!5GmcNpJ9M`&&j^Z@kK$*UwrA(XT8_DD6R^rZH7pbQx4K<+K1J&>r`~+% z>8{oi`_I_z3v_+^>X%&YKXZ-(skc8;6zSOG7dZ#6dGLYiw)4I|?djit-yQy~=QF*U z?}-Oe-Wy5ulz&aoOk0vESRbUmNDJouZ0oK)#~l2FEpKPPfxLrK3Br3V5l zN`YSjES3D7`@qG|A|(5BA%IkNzPS&)40sWe{aFCu5E}&Fg4EB+a@r0J3SSjR@To=; z+!?A^XF`yv;n@#oA;+nPozE%xKF*CJi0Hhk>a25Cuc}(pP+i|pRpnIGuBvTts+J>j zs%9^oSGBCBzUHjjH4QcO3+K&Pxq4O2!s;_u)&ym#ORAQxuNDKHXb zUtL$Vx~`_ax}kRUDrb3pO^tI-%{fc!&UdP+s@JTkLFLPZMNI z>NU0NtDpzdXRUHp)}C3{u%f=EdfBKot4Eaxzf`CR&arc6PoGgWW>o1oLyj3WURo@} zHlmJEO}iL_@)vwupuYmi^WfU8*JF4_oo6(*_JCS+Zp3*>%X7Zq`CXY86ztaX2=QEv zJDex9KKW-)2mm=3^r@wQt~gxHi&}3-9F9?7KHYIR>z2aZaX1=aJ_9QQvKKP;fgnY3 zxSXr}jEKWI$4FU893JmySsX5H<7ZkNE_L%$5r@b7$?~4Orw4j^pr;3VdZ4EVdU~Lz z2YPzof2Id2HolQjab@i3qaCMWb9+N<$8o5qV&hLU9trB(JN{)9?H%?CjyZ+XKncG> z3U_wx#b?-a!poL&XNTf{BfM;FceX437sAWda%Y?39~NG=lsj7$|Df=)mE75)`1^#H zE#%H7@D*F8{uh9XEwQgjHfeXm0JwXw&~P_%Z&yxX6J(u7bPoCRmHha6$wuy}Xzs4q z_UEH3w(X8qxQ|vmxu>B&47`WSc`=Z&w`+M$;mlCKY3d~CIcrBRs@OR78M)}MX#P_} zADI0OF1dG{0he~Hz~RwY9^~9JSTFOP7ou?asGP!0;+4}w^eM$ z@!^aob2eX(Eqlf0y*ZnrUwR?qCed-j(3O~CFcz6)5+$hpOrS8RN_NeWLd-i?Cq7C)d078c(K z?nlMj!BuRzuL+q5Eh{d|8F_l~T|%!cuBh13rzB_Ovf>AXZzx_Y*+s=GBwJX#K4;|0 z;_Y*reT)YK?q~wBq7{cFZ8$7$$Dy)gd3nxT zAIafeuoYc6OzReQmmK~mhv((+)bgCNKU`FNxzN9q!y^JT06?}f$d(1!=|Q$I$j|{I zGBe07l2(hJ%2o#1vLHJ>$W+Ia{b6pv%?z@O&?8DuWh(=2S&&I@p=@E0%?+}d$jX8K zO&$NDj&G>rE9&?Mb^NV5{z@Hxs*WG4<9q7(raJyf9bZt#r`7Rsb^Mt+?or40)$z~j z_^LW~s^c^2_-l21R2}!K;|J>amO8$sjxVa?@6_=Lb$m=6|EiAfsN?JE__8`ar;fi- z$6u)9!|M2xUb$W9Nkg+Vqq$YvHlE9djtvOs%Bpsli%#VT7CWTyw&!XTR)WHXUre7vw*9lI+w zzTTv$0PV`jyCC47Rj8aLWiM=3$M&4O`j(u$FSh37o!OR?cPjV=9XWY(pfkO@V$$}U z&6ode@7~$&JLMZ+YQzMRlXK|~OwV$TVg|tZ<<8#r$F$%K&X84Xekuob z!agiPUpGqlO~I_CD4DAy6zlp2B{D??luS>cUf4E!OL=#BPF|#9<8HL5A6kT_l;;MX z2hIi#%CTLP|7V=&f)mp*n~4`!VHp%Jiav8MI>d{jz>9S4MagIGMZ0)W5_r*sErRsL zw9nj&Ht}Lw;Kg4tT*QmY&)kbv@uE_^Shrq0Sn}C=Ahj>S8J~kQ-kmk*`HG8wDjSUQ z51uOD_{ZqX=4WyG$v&w3hq9FuzM^^C&KEwyW+vRt

7p@-N=cjOf|Vbjwgkv7hO* z_cNWrex@_n&vXX+na*H8(;4h%I)nX8XRx1vg&Q%|K~H7Deg=Aq3-&Y6Q(Um0fu7=m z{S5T(67^s|13kqB`x)pdF4)gNPwi(q)qbW^?PogGex_6HXFAn>rc>=_I@NxrQ|)Iu z)qbW^?PogGex_6HXFAn>rc>=_I@NxrQ|)Iu)qbW^?PogGex_6HXFAn>rc>=_I@Nxr zQ|)Iu)qbW^?PogGex_6HXFAn>rc>=_I@NxrQ|)Iu)qbW^?PogGex_6HXFAn>rc>=_ zI@NxrQ|)Iu)qbW^?PogGex_6HXFAn>rc>=_I@NxrGjTtI$UG24MrFZ%20g5}U_XN% zR$Q>3K@TVHXJBD_U_oWUeg+m47wl(XL2yD`?wUNrle7XuV~lVCsd;6Hw*cI9OgDZQIRvVW5@5_*t@rR=iDs^{S~iB%-u5JF=Vrww^cN6ulV5E zd0Wyh-Zh;5Z2UN5ZRWOmrH0_ePZU^s+m11g< zePQ#%6&oMP-PoRny=&UFIU{Fu&z#g%zq34NPo4BwHnrYRC*XymL)MYagJM3GHG)(N?>4mZ=lZEiy6C}4>OS^$)9 z+|#?^cm-E#xRr9#G|&bBHvt-kE3p!dwkx#n+B}68X|w}q1=_i-E3M*#=ky)4w7ls# zBWI4t#eP{LTH1cv8Re&!pHcn=yi~aQSoXIb?PYB1;@|-o#;?AA-=*|-8FKy|P~A90!8{9C!Zy6GH6e_VmnapRtWIh!_v2UV1_=_%MNe@N~tgU@X!z&-8}Gn=~^ z&hjeU!NoX#Y-VvEc{i#2AsIRkmErIc*l>0XMdh=b(~2u_D|T~x&ZgtV@|M^e??{v{ zD$bqT{LVwU5L{l7vuZo;Fkd-B%KQEeY88*o7|0@=#4~cQL?z2}R&LA5i#E?F*|B@a z-o3aNJr{CCu;9!p-CO<;PDacJdv{!k!ip;|9I@jP5VNnGQoQ4Q5Id);08LZ=$_rEF z4|U_X^Ek$B?v=`?3ybE-ZRYDyCSDGtmpeA8I@eZKT-kuT#m#HbmG3vy&lJxv^EAJX zs<-Wo>^KM)faT4PN`rri@i_O&3yXFxSCQ7^vHT(Fot@u8q3S=h0Sy;_qyal8(;w+q zdEcx2p$@@sr@WTy2RThHMUeyv&n3dg5YXxQ^z=Ya5A^gvPY?9;Ku-_+t9oEf{nF9u z)!k7`oe{Om)*m&sbY#(?hZYGvWol{3NL|s05yziBb?UgF?D+brQZ%yYqKk@xf(da2 zW8w?S;tIx%9O?Y#y}f%SwIa13H6hg@EkUY8nub(@l#BGHPCrBbIMUNdFChI1>0Tsp zZOz&#~x15vgKX~o-n z_fA0W9Fv`UX=F)u#+*l$-Bv(Xp{!2csF2srIlxoxHL8+yJTbf zob0wp^fzwr?2;MTBg(Ujre_a??DXu6W91#<3ZxxK-~G$pz3M%4lwBQx%$K6mvx~N* zP0t>2RjfR_nHhWHGH8u;I*1_SHPOqZcD)mHoR87@NjhJy^L0AEROi?0{MZ>YCKrvsH^N62 zm6nXcH*<@|l#D4YDIHrGOY3WGkaMT-wCg|WL`s=e37!S*@J4WlT z|2r9O4f?Ln9%whClT#^1j)QjW$%0A9q8YtDfLK~utaep{6PqTGn;|c|MI*fy!bmi& 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 diff --git a/resources/lib/deps/Cryptodome/Cipher/_raw_arc2.abi3.so b/resources/lib/deps/Cryptodome/Cipher/_raw_arc2.abi3.so deleted file mode 100755 index 2287d2ebaf45bbface2591f26a3de725bfe2a40a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46464 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 ZZc(5`QeevkCH`opSlT*TFjypo{|B?PrRe|w diff --git a/resources/lib/deps/Cryptodome/Cipher/_raw_cast.abi3.so b/resources/lib/deps/Cryptodome/Cipher/_raw_cast.abi3.so deleted file mode 100755 index 730e178e024a73529fed764615ae91fcaa08e6a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57408 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 bxY^)dGj(WU@rx$Ae)CpCe}hTE#ESm`IH3}L diff --git a/resources/lib/deps/Cryptodome/Cipher/_raw_cfb.abi3.so b/resources/lib/deps/Cryptodome/Cipher/_raw_cfb.abi3.so deleted file mode 100755 index 2c9b852c4dc9ceeb168d0812b4187c228a422c40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26864 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 RP!r0Z=&__~V+3MQ@t>gs81n!C diff --git a/resources/lib/deps/Cryptodome/Cipher/_raw_des.abi3.so b/resources/lib/deps/Cryptodome/Cipher/_raw_des.abi3.so deleted file mode 100755 index 7f1f8246c45e5e8d893b35997b8bd06dd7fbb2a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71560 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 diff --git a/resources/lib/deps/Cryptodome/Cipher/_raw_des3.abi3.so b/resources/lib/deps/Cryptodome/Cipher/_raw_des3.abi3.so deleted file mode 100755 index b475c52c6c9233431af8ce533664230f918c5a60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 72520 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 diff --git a/resources/lib/deps/Cryptodome/Cipher/_raw_ecb.abi3.so b/resources/lib/deps/Cryptodome/Cipher/_raw_ecb.abi3.so deleted file mode 100755 index 91e81262dc37862f0250fa1ce3941f5b92834ea0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19016 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 MH|!RNfrDlL0T9n0a{vGU diff --git a/resources/lib/deps/Cryptodome/Cipher/_raw_eksblowfish.abi3.so b/resources/lib/deps/Cryptodome/Cipher/_raw_eksblowfish.abi3.so deleted file mode 100755 index c3c45d52cf35fe5b98fcf1445bf4492691c306fd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 181192 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 diff --git a/resources/lib/deps/Cryptodome/Cipher/_raw_ofb.abi3.so b/resources/lib/deps/Cryptodome/Cipher/_raw_ofb.abi3.so deleted file mode 100755 index a4a629a8e70deddb7c744c0b21ecd36eb761570e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22128 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 diff --git a/resources/lib/deps/Cryptodome/Hash/BLAKE2b.py b/resources/lib/deps/Cryptodome/Hash/BLAKE2b.py deleted file mode 100644 index 85da887..0000000 --- a/resources/lib/deps/Cryptodome/Hash/BLAKE2b.py +++ /dev/null @@ -1,247 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from binascii import unhexlify - -from Cryptodome.Util.py3compat import bord, tobytes - -from Cryptodome.Random import get_random_bytes -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr) - -_raw_blake2b_lib = load_pycryptodome_raw_lib("Cryptodome.Hash._BLAKE2b", - """ - int blake2b_init(void **state, - const uint8_t *key, - size_t key_size, - size_t digest_size); - int blake2b_destroy(void *state); - int blake2b_update(void *state, - const uint8_t *buf, - size_t len); - int blake2b_digest(const void *state, - uint8_t digest[64]); - int blake2b_copy(const void *src, void *dst); - """) - - -class BLAKE2b_Hash(object): - """A BLAKE2b hash object. - Do not instantiate directly. Use the :func:`new` function. - - :ivar oid: ASN.1 Object ID - :vartype oid: string - - :ivar block_size: the size in bytes of the internal message block, - input to the compression function - :vartype block_size: integer - - :ivar digest_size: the size in bytes of the resulting hash - :vartype digest_size: integer - """ - - # The internal block size of the hash algorithm in bytes. - block_size = 64 - - def __init__(self, data, key, digest_bytes, update_after_digest): - - # The size of the resulting hash in bytes. - self.digest_size = digest_bytes - - self._update_after_digest = update_after_digest - self._digest_done = False - - # See https://tools.ietf.org/html/rfc7693 - if digest_bytes in (20, 32, 48, 64) and not key: - self.oid = "1.3.6.1.4.1.1722.12.2.1." + str(digest_bytes) - - state = VoidPointer() - result = _raw_blake2b_lib.blake2b_init(state.address_of(), - c_uint8_ptr(key), - c_size_t(len(key)), - c_size_t(digest_bytes) - ) - if result: - raise ValueError("Error %d while instantiating BLAKE2b" % result) - self._state = SmartPointer(state.get(), - _raw_blake2b_lib.blake2b_destroy) - if data: - self.update(data) - - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Args: - data (bytes/bytearray/memoryview): The next chunk of the message being hashed. - """ - - if self._digest_done and not self._update_after_digest: - raise TypeError("You can only call 'digest' or 'hexdigest' on this object") - - result = _raw_blake2b_lib.blake2b_update(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data))) - if result: - raise ValueError("Error %d while hashing BLAKE2b data" % result) - return self - - - def digest(self): - """Return the **binary** (non-printable) digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Binary form. - :rtype: byte string - """ - - bfr = create_string_buffer(64) - result = _raw_blake2b_lib.blake2b_digest(self._state.get(), - bfr) - if result: - raise ValueError("Error %d while creating BLAKE2b digest" % result) - - self._digest_done = True - - return get_raw_buffer(bfr)[:self.digest_size] - - - def hexdigest(self): - """Return the **printable** digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) for x in tuple(self.digest())]) - - - def verify(self, mac_tag): - """Verify that a given **binary** MAC (computed by another party) - is valid. - - Args: - mac_tag (bytes/bytearray/memoryview): the expected MAC of the message. - - Raises: - ValueError: if the MAC does not match. It means that the message - has been tampered with or that the MAC key is incorrect. - """ - - secret = get_random_bytes(16) - - mac1 = new(digest_bits=160, key=secret, data=mac_tag) - mac2 = new(digest_bits=160, key=secret, data=self.digest()) - - if mac1.digest() != mac2.digest(): - raise ValueError("MAC check failed") - - - def hexverify(self, hex_mac_tag): - """Verify that a given **printable** MAC (computed by another party) - is valid. - - Args: - hex_mac_tag (string): the expected MAC of the message, as a hexadecimal string. - - Raises: - ValueError: if the MAC does not match. It means that the message - has been tampered with or that the MAC key is incorrect. - """ - - self.verify(unhexlify(tobytes(hex_mac_tag))) - - - def new(self, **kwargs): - """Return a new instance of a BLAKE2b hash object. - See :func:`new`. - """ - - if "digest_bytes" not in kwargs and "digest_bits" not in kwargs: - kwargs["digest_bytes"] = self.digest_size - - return new(**kwargs) - - -def new(**kwargs): - """Create a new hash object. - - Args: - data (bytes/bytearray/memoryview): - Optional. The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`BLAKE2b_Hash.update`. - digest_bytes (integer): - Optional. The size of the digest, in bytes (1 to 64). Default is 64. - digest_bits (integer): - Optional and alternative to ``digest_bytes``. - The size of the digest, in bits (8 to 512, in steps of 8). - Default is 512. - key (bytes/bytearray/memoryview): - Optional. The key to use to compute the MAC (1 to 64 bytes). - If not specified, no key will be used. - update_after_digest (boolean): - Optional. By default, a hash object cannot be updated anymore after - the digest is computed. When this flag is ``True``, such check - is no longer enforced. - - Returns: - A :class:`BLAKE2b_Hash` hash object - """ - - data = kwargs.pop("data", None) - update_after_digest = kwargs.pop("update_after_digest", False) - - digest_bytes = kwargs.pop("digest_bytes", None) - digest_bits = kwargs.pop("digest_bits", None) - if None not in (digest_bytes, digest_bits): - raise TypeError("Only one digest parameter must be provided") - if (None, None) == (digest_bytes, digest_bits): - digest_bytes = 64 - if digest_bytes is not None: - if not (1 <= digest_bytes <= 64): - raise ValueError("'digest_bytes' not in range 1..64") - else: - if not (8 <= digest_bits <= 512) or (digest_bits % 8): - raise ValueError("'digest_bits' not in range 8..512, " - "with steps of 8") - digest_bytes = digest_bits // 8 - - key = kwargs.pop("key", b"") - if len(key) > 64: - raise ValueError("BLAKE2b key cannot exceed 64 bytes") - - if kwargs: - raise TypeError("Unknown parameters: " + str(kwargs)) - - return BLAKE2b_Hash(data, key, digest_bytes, update_after_digest) diff --git a/resources/lib/deps/Cryptodome/Hash/BLAKE2b.pyi b/resources/lib/deps/Cryptodome/Hash/BLAKE2b.pyi deleted file mode 100644 index d37c374..0000000 --- a/resources/lib/deps/Cryptodome/Hash/BLAKE2b.pyi +++ /dev/null @@ -1,32 +0,0 @@ -from typing import Any, Union -from types import ModuleType - -Buffer = Union[bytes, bytearray, memoryview] - -class BLAKE2b_Hash(object): - block_size: int - digest_size: int - oid: str - - def __init__(self, - data: Buffer, - key: Buffer, - digest_bytes: bytes, - update_after_digest: bool) -> None: ... - def update(self, data: Buffer) -> BLAKE2b_Hash: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def verify(self, mac_tag: Buffer) -> None: ... - def hexverify(self, hex_mac_tag: str) -> None: ... - def new(self, - data: Buffer = ..., - digest_bytes: int = ..., - digest_bits: int = ..., - key: Buffer = ..., - update_after_digest: bool = ...) -> BLAKE2b_Hash: ... - -def new(data: Buffer = ..., - digest_bytes: int = ..., - digest_bits: int = ..., - key: Buffer = ..., - update_after_digest: bool = ...) -> BLAKE2b_Hash: ... diff --git a/resources/lib/deps/Cryptodome/Hash/BLAKE2s.py b/resources/lib/deps/Cryptodome/Hash/BLAKE2s.py deleted file mode 100644 index 43be5c4..0000000 --- a/resources/lib/deps/Cryptodome/Hash/BLAKE2s.py +++ /dev/null @@ -1,247 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from binascii import unhexlify - -from Cryptodome.Util.py3compat import bord, tobytes - -from Cryptodome.Random import get_random_bytes -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr) - -_raw_blake2s_lib = load_pycryptodome_raw_lib("Cryptodome.Hash._BLAKE2s", - """ - int blake2s_init(void **state, - const uint8_t *key, - size_t key_size, - size_t digest_size); - int blake2s_destroy(void *state); - int blake2s_update(void *state, - const uint8_t *buf, - size_t len); - int blake2s_digest(const void *state, - uint8_t digest[32]); - int blake2s_copy(const void *src, void *dst); - """) - - -class BLAKE2s_Hash(object): - """A BLAKE2s hash object. - Do not instantiate directly. Use the :func:`new` function. - - :ivar oid: ASN.1 Object ID - :vartype oid: string - - :ivar block_size: the size in bytes of the internal message block, - input to the compression function - :vartype block_size: integer - - :ivar digest_size: the size in bytes of the resulting hash - :vartype digest_size: integer - """ - - # The internal block size of the hash algorithm in bytes. - block_size = 32 - - def __init__(self, data, key, digest_bytes, update_after_digest): - - # The size of the resulting hash in bytes. - self.digest_size = digest_bytes - - self._update_after_digest = update_after_digest - self._digest_done = False - - # See https://tools.ietf.org/html/rfc7693 - if digest_bytes in (16, 20, 28, 32) and not key: - self.oid = "1.3.6.1.4.1.1722.12.2.2." + str(digest_bytes) - - state = VoidPointer() - result = _raw_blake2s_lib.blake2s_init(state.address_of(), - c_uint8_ptr(key), - c_size_t(len(key)), - c_size_t(digest_bytes) - ) - if result: - raise ValueError("Error %d while instantiating BLAKE2s" % result) - self._state = SmartPointer(state.get(), - _raw_blake2s_lib.blake2s_destroy) - if data: - self.update(data) - - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Args: - data (byte string/byte array/memoryview): The next chunk of the message being hashed. - """ - - if self._digest_done and not self._update_after_digest: - raise TypeError("You can only call 'digest' or 'hexdigest' on this object") - - result = _raw_blake2s_lib.blake2s_update(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data))) - if result: - raise ValueError("Error %d while hashing BLAKE2s data" % result) - return self - - - def digest(self): - """Return the **binary** (non-printable) digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Binary form. - :rtype: byte string - """ - - bfr = create_string_buffer(32) - result = _raw_blake2s_lib.blake2s_digest(self._state.get(), - bfr) - if result: - raise ValueError("Error %d while creating BLAKE2s digest" % result) - - self._digest_done = True - - return get_raw_buffer(bfr)[:self.digest_size] - - - def hexdigest(self): - """Return the **printable** digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) for x in tuple(self.digest())]) - - - def verify(self, mac_tag): - """Verify that a given **binary** MAC (computed by another party) - is valid. - - Args: - mac_tag (byte string/byte array/memoryview): the expected MAC of the message. - - Raises: - ValueError: if the MAC does not match. It means that the message - has been tampered with or that the MAC key is incorrect. - """ - - secret = get_random_bytes(16) - - mac1 = new(digest_bits=160, key=secret, data=mac_tag) - mac2 = new(digest_bits=160, key=secret, data=self.digest()) - - if mac1.digest() != mac2.digest(): - raise ValueError("MAC check failed") - - - def hexverify(self, hex_mac_tag): - """Verify that a given **printable** MAC (computed by another party) - is valid. - - Args: - hex_mac_tag (string): the expected MAC of the message, as a hexadecimal string. - - Raises: - ValueError: if the MAC does not match. It means that the message - has been tampered with or that the MAC key is incorrect. - """ - - self.verify(unhexlify(tobytes(hex_mac_tag))) - - - def new(self, **kwargs): - """Return a new instance of a BLAKE2s hash object. - See :func:`new`. - """ - - if "digest_bytes" not in kwargs and "digest_bits" not in kwargs: - kwargs["digest_bytes"] = self.digest_size - - return new(**kwargs) - - -def new(**kwargs): - """Create a new hash object. - - Args: - data (byte string/byte array/memoryview): - Optional. The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`BLAKE2s_Hash.update`. - digest_bytes (integer): - Optional. The size of the digest, in bytes (1 to 32). Default is 32. - digest_bits (integer): - Optional and alternative to ``digest_bytes``. - The size of the digest, in bits (8 to 256, in steps of 8). - Default is 256. - key (byte string): - Optional. The key to use to compute the MAC (1 to 64 bytes). - If not specified, no key will be used. - update_after_digest (boolean): - Optional. By default, a hash object cannot be updated anymore after - the digest is computed. When this flag is ``True``, such check - is no longer enforced. - - Returns: - A :class:`BLAKE2s_Hash` hash object - """ - - data = kwargs.pop("data", None) - update_after_digest = kwargs.pop("update_after_digest", False) - - digest_bytes = kwargs.pop("digest_bytes", None) - digest_bits = kwargs.pop("digest_bits", None) - if None not in (digest_bytes, digest_bits): - raise TypeError("Only one digest parameter must be provided") - if (None, None) == (digest_bytes, digest_bits): - digest_bytes = 32 - if digest_bytes is not None: - if not (1 <= digest_bytes <= 32): - raise ValueError("'digest_bytes' not in range 1..32") - else: - if not (8 <= digest_bits <= 256) or (digest_bits % 8): - raise ValueError("'digest_bits' not in range 8..256, " - "with steps of 8") - digest_bytes = digest_bits // 8 - - key = kwargs.pop("key", b"") - if len(key) > 32: - raise ValueError("BLAKE2s key cannot exceed 32 bytes") - - if kwargs: - raise TypeError("Unknown parameters: " + str(kwargs)) - - return BLAKE2s_Hash(data, key, digest_bytes, update_after_digest) diff --git a/resources/lib/deps/Cryptodome/Hash/BLAKE2s.pyi b/resources/lib/deps/Cryptodome/Hash/BLAKE2s.pyi deleted file mode 100644 index 374b3a4..0000000 --- a/resources/lib/deps/Cryptodome/Hash/BLAKE2s.pyi +++ /dev/null @@ -1,26 +0,0 @@ -from typing import Any, Union - -Buffer = Union[bytes, bytearray, memoryview] - -class BLAKE2s_Hash(object): - block_size: int - digest_size: int - oid: str - - def __init__(self, - data: Buffer, - key: Buffer, - digest_bytes: bytes, - update_after_digest: bool) -> None: ... - def update(self, data: Buffer) -> BLAKE2s_Hash: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def verify(self, mac_tag: Buffer) -> None: ... - def hexverify(self, hex_mac_tag: str) -> None: ... - def new(self, **kwargs: Any) -> BLAKE2s_Hash: ... - -def new(data: Buffer = ..., - digest_bytes: int = ..., - digest_bits: int = ..., - key: Buffer = ..., - update_after_digest: bool = ...) -> BLAKE2s_Hash: ... diff --git a/resources/lib/deps/Cryptodome/Hash/CMAC.py b/resources/lib/deps/Cryptodome/Hash/CMAC.py deleted file mode 100644 index 8feb79f..0000000 --- a/resources/lib/deps/Cryptodome/Hash/CMAC.py +++ /dev/null @@ -1,306 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Hash/CMAC.py - Implements the CMAC algorithm -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -from binascii import unhexlify - -from Cryptodome.Hash import BLAKE2s -from Cryptodome.Util.strxor import strxor -from Cryptodome.Util.number import long_to_bytes, bytes_to_long -from Cryptodome.Util.py3compat import bord, tobytes, _copy_bytes -from Cryptodome.Random import get_random_bytes - - -# The size of the authentication tag produced by the MAC. -digest_size = None - - -def _shift_bytes(bs, xor_lsb=0): - num = (bytes_to_long(bs) << 1) ^ xor_lsb - return long_to_bytes(num, len(bs))[-len(bs):] - - -class CMAC(object): - """A CMAC hash object. - Do not instantiate directly. Use the :func:`new` function. - - :ivar digest_size: the size in bytes of the resulting MAC tag - :vartype digest_size: integer - """ - - digest_size = None - - def __init__(self, key, msg, ciphermod, cipher_params, mac_len, - update_after_digest): - - self.digest_size = mac_len - - self._key = _copy_bytes(None, None, key) - self._factory = ciphermod - self._cipher_params = cipher_params - self._block_size = bs = ciphermod.block_size - self._mac_tag = None - self._update_after_digest = update_after_digest - - # Section 5.3 of NIST SP 800 38B and Appendix B - if bs == 8: - const_Rb = 0x1B - self._max_size = 8 * (2 ** 21) - elif bs == 16: - const_Rb = 0x87 - self._max_size = 16 * (2 ** 48) - else: - raise TypeError("CMAC requires a cipher with a block size" - " of 8 or 16 bytes, not %d" % bs) - - # Compute sub-keys - zero_block = b'\x00' * bs - self._ecb = ciphermod.new(key, - ciphermod.MODE_ECB, - **self._cipher_params) - L = self._ecb.encrypt(zero_block) - if bord(L[0]) & 0x80: - self._k1 = _shift_bytes(L, const_Rb) - else: - self._k1 = _shift_bytes(L) - if bord(self._k1[0]) & 0x80: - self._k2 = _shift_bytes(self._k1, const_Rb) - else: - self._k2 = _shift_bytes(self._k1) - - # Initialize CBC cipher with zero IV - self._cbc = ciphermod.new(key, - ciphermod.MODE_CBC, - zero_block, - **self._cipher_params) - - # Cache for outstanding data to authenticate - self._cache = bytearray(bs) - self._cache_n = 0 - - # Last piece of ciphertext produced - self._last_ct = zero_block - - # Last block that was encrypted with AES - self._last_pt = None - - # Counter for total message size - self._data_size = 0 - - if msg: - self.update(msg) - - def update(self, msg): - """Authenticate the next chunk of message. - - Args: - data (byte string/byte array/memoryview): The next chunk of data - """ - - if self._mac_tag is not None and not self._update_after_digest: - raise TypeError("update() cannot be called after digest() or verify()") - - self._data_size += len(msg) - bs = self._block_size - - if self._cache_n > 0: - filler = min(bs - self._cache_n, len(msg)) - self._cache[self._cache_n:self._cache_n+filler] = msg[:filler] - self._cache_n += filler - - if self._cache_n < bs: - return self - - msg = memoryview(msg)[filler:] - self._update(self._cache) - self._cache_n = 0 - - remain = len(msg) % bs - if remain > 0: - self._update(msg[:-remain]) - self._cache[:remain] = msg[-remain:] - else: - self._update(msg) - self._cache_n = remain - return self - - def _update(self, data_block): - """Update a block aligned to the block boundary""" - - bs = self._block_size - assert len(data_block) % bs == 0 - - if len(data_block) == 0: - return - - ct = self._cbc.encrypt(data_block) - if len(data_block) == bs: - second_last = self._last_ct - else: - second_last = ct[-bs*2:-bs] - self._last_ct = ct[-bs:] - self._last_pt = strxor(second_last, data_block[-bs:]) - - def copy(self): - """Return a copy ("clone") of the CMAC object. - - The copy will have the same internal state as the original CMAC - object. - This can be used to efficiently compute the MAC tag of byte - strings that share a common initial substring. - - :return: An :class:`CMAC` - """ - - obj = self.__new__(CMAC) - obj.__dict__ = self.__dict__.copy() - obj._cbc = self._factory.new(self._key, - self._factory.MODE_CBC, - self._last_ct, - **self._cipher_params) - obj._cache = self._cache[:] - obj._last_ct = self._last_ct[:] - return obj - - def digest(self): - """Return the **binary** (non-printable) MAC tag of the message - that has been authenticated so far. - - :return: The MAC tag, computed over the data processed so far. - Binary form. - :rtype: byte string - """ - - bs = self._block_size - - if self._mac_tag is not None and not self._update_after_digest: - return self._mac_tag - - if self._data_size > self._max_size: - raise ValueError("MAC is unsafe for this message") - - if self._cache_n == 0 and self._data_size > 0: - # Last block was full - pt = strxor(self._last_pt, self._k1) - else: - # Last block is partial (or message length is zero) - partial = self._cache[:] - partial[self._cache_n:] = b'\x80' + b'\x00' * (bs - self._cache_n - 1) - pt = strxor(strxor(self._last_ct, partial), self._k2) - - self._mac_tag = self._ecb.encrypt(pt)[:self.digest_size] - - return self._mac_tag - - def hexdigest(self): - """Return the **printable** MAC tag of the message authenticated so far. - - :return: The MAC tag, computed over the data processed so far. - Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) - for x in tuple(self.digest())]) - - def verify(self, mac_tag): - """Verify that a given **binary** MAC (computed by another party) - is valid. - - Args: - mac_tag (byte string/byte array/memoryview): the expected MAC of the message. - - Raises: - ValueError: if the MAC does not match. It means that the message - has been tampered with or that the MAC key is incorrect. - """ - - secret = get_random_bytes(16) - - mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=mac_tag) - mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=self.digest()) - - if mac1.digest() != mac2.digest(): - raise ValueError("MAC check failed") - - def hexverify(self, hex_mac_tag): - """Verify that a given **printable** MAC (computed by another party) - is valid. - - Args: - hex_mac_tag (string): the expected MAC of the message, as a hexadecimal string. - - Raises: - ValueError: if the MAC does not match. It means that the message - has been tampered with or that the MAC key is incorrect. - """ - - self.verify(unhexlify(tobytes(hex_mac_tag))) - - -def new(key, msg=None, ciphermod=None, cipher_params=None, mac_len=None, - update_after_digest=False): - """Create a new MAC object. - - Args: - key (byte string/byte array/memoryview): - key for the CMAC object. - The key must be valid for the underlying cipher algorithm. - For instance, it must be 16 bytes long for AES-128. - ciphermod (module): - A cipher module from :mod:`Cryptodome.Cipher`. - The cipher's block size has to be 128 bits, - like :mod:`Cryptodome.Cipher.AES`, to reduce the probability - of collisions. - msg (byte string/byte array/memoryview): - Optional. The very first chunk of the message to authenticate. - It is equivalent to an early call to `CMAC.update`. Optional. - cipher_params (dict): - Optional. A set of parameters to use when instantiating a cipher - object. - mac_len (integer): - Length of the MAC, in bytes. - It must be at least 4 bytes long. - The default (and recommended) length matches the size of a cipher block. - update_after_digest (boolean): - Optional. By default, a hash object cannot be updated anymore after - the digest is computed. When this flag is ``True``, such check - is no longer enforced. - Returns: - A :class:`CMAC` object - """ - - if ciphermod is None: - raise TypeError("ciphermod must be specified (try AES)") - - cipher_params = {} if cipher_params is None else dict(cipher_params) - - if mac_len is None: - mac_len = ciphermod.block_size - - if mac_len < 4: - raise ValueError("MAC tag length must be at least 4 bytes long") - - if mac_len > ciphermod.block_size: - raise ValueError("MAC tag length cannot be larger than a cipher block (%d) bytes" % ciphermod.block_size) - - return CMAC(key, msg, ciphermod, cipher_params, mac_len, - update_after_digest) diff --git a/resources/lib/deps/Cryptodome/Hash/CMAC.pyi b/resources/lib/deps/Cryptodome/Hash/CMAC.pyi deleted file mode 100644 index acdf055..0000000 --- a/resources/lib/deps/Cryptodome/Hash/CMAC.pyi +++ /dev/null @@ -1,30 +0,0 @@ -from types import ModuleType -from typing import Union, Dict, Any - -Buffer = Union[bytes, bytearray, memoryview] - -digest_size: int - -class CMAC(object): - digest_size: int - - def __init__(self, - key: Buffer, - msg: Buffer, - ciphermod: ModuleType, - cipher_params: Dict[str, Any], - mac_len: int, update_after_digest: bool) -> None: ... - def update(self, data: Buffer) -> CMAC: ... - def copy(self) -> CMAC: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def verify(self, mac_tag: Buffer) -> None: ... - def hexverify(self, hex_mac_tag: str) -> None: ... - - -def new(key: Buffer, - msg: Buffer = ..., - ciphermod: ModuleType = ..., - cipher_params: Dict[str, Any] = ..., - mac_len: int = ..., - update_after_digest: bool = ...) -> CMAC: ... diff --git a/resources/lib/deps/Cryptodome/Hash/HMAC.py b/resources/lib/deps/Cryptodome/Hash/HMAC.py deleted file mode 100644 index 615056a..0000000 --- a/resources/lib/deps/Cryptodome/Hash/HMAC.py +++ /dev/null @@ -1,238 +0,0 @@ -# -# HMAC.py - Implements the HMAC algorithm as described by RFC 2104. -# -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from Cryptodome.Util.py3compat import bord, tobytes - -from binascii import unhexlify - -from Cryptodome.Hash import BLAKE2s -from Cryptodome.Util.strxor import strxor -from Cryptodome.Random import get_random_bytes - -__all__ = ['new', 'HMAC'] - -_hash2hmac_oid = { - '1.3.14.3.2.26': '1.2.840.113549.2.7', # SHA-1 - '2.16.840.1.101.3.4.2.4': '1.2.840.113549.2.8', # SHA-224 - '2.16.840.1.101.3.4.2.1': '1.2.840.113549.2.9', # SHA-256 - '2.16.840.1.101.3.4.2.2': '1.2.840.113549.2.10', # SHA-384 - '2.16.840.1.101.3.4.2.3': '1.2.840.113549.2.11', # SHA-512 - '2.16.840.1.101.3.4.2.5': '1.2.840.113549.2.12', # SHA-512_224 - '2.16.840.1.101.3.4.2.6': '1.2.840.113549.2.13', # SHA-512_256 - '2.16.840.1.101.3.4.2.7': '2.16.840.1.101.3.4.2.13', # SHA-3 224 - '2.16.840.1.101.3.4.2.8': '2.16.840.1.101.3.4.2.14', # SHA-3 256 - '2.16.840.1.101.3.4.2.9': '2.16.840.1.101.3.4.2.15', # SHA-3 384 - '2.16.840.1.101.3.4.2.10': '2.16.840.1.101.3.4.2.16', # SHA-3 512 -} - -_hmac2hash_oid = {v: k for k, v in _hash2hmac_oid.items()} - - -class HMAC(object): - """An HMAC hash object. - Do not instantiate directly. Use the :func:`new` function. - - :ivar digest_size: the size in bytes of the resulting MAC tag - :vartype digest_size: integer - - :ivar oid: the ASN.1 object ID of the HMAC algorithm. - Only present if the algorithm was officially assigned one. - """ - - def __init__(self, key, msg=b"", digestmod=None): - - if digestmod is None: - from Cryptodome.Hash import MD5 - digestmod = MD5 - - if msg is None: - msg = b"" - - # Size of the MAC tag - self.digest_size = digestmod.digest_size - - self._digestmod = digestmod - - # Hash OID --> HMAC OID - try: - self.oid = _hash2hmac_oid[digestmod.oid] - except (KeyError, AttributeError): - pass - - if isinstance(key, memoryview): - key = key.tobytes() - - try: - if len(key) <= digestmod.block_size: - # Step 1 or 2 - key_0 = key + b"\x00" * (digestmod.block_size - len(key)) - else: - # Step 3 - hash_k = digestmod.new(key).digest() - key_0 = hash_k + b"\x00" * (digestmod.block_size - len(hash_k)) - except AttributeError: - # Not all hash types have "block_size" - raise ValueError("Hash type incompatible to HMAC") - - # Step 4 - key_0_ipad = strxor(key_0, b"\x36" * len(key_0)) - - # Start step 5 and 6 - self._inner = digestmod.new(key_0_ipad) - self._inner.update(msg) - - # Step 7 - key_0_opad = strxor(key_0, b"\x5c" * len(key_0)) - - # Start step 8 and 9 - self._outer = digestmod.new(key_0_opad) - - def update(self, msg): - """Authenticate the next chunk of message. - - Args: - data (byte string/byte array/memoryview): The next chunk of data - """ - - self._inner.update(msg) - return self - - def _pbkdf2_hmac_assist(self, first_digest, iterations): - """Carry out the expensive inner loop for PBKDF2-HMAC""" - - result = self._digestmod._pbkdf2_hmac_assist( - self._inner, - self._outer, - first_digest, - iterations) - return result - - def copy(self): - """Return a copy ("clone") of the HMAC object. - - The copy will have the same internal state as the original HMAC - object. - This can be used to efficiently compute the MAC tag of byte - strings that share a common initial substring. - - :return: An :class:`HMAC` - """ - - new_hmac = HMAC(b"fake key", digestmod=self._digestmod) - - # Syncronize the state - new_hmac._inner = self._inner.copy() - new_hmac._outer = self._outer.copy() - - return new_hmac - - def digest(self): - """Return the **binary** (non-printable) MAC tag of the message - authenticated so far. - - :return: The MAC tag digest, computed over the data processed so far. - Binary form. - :rtype: byte string - """ - - frozen_outer_hash = self._outer.copy() - frozen_outer_hash.update(self._inner.digest()) - return frozen_outer_hash.digest() - - def verify(self, mac_tag): - """Verify that a given **binary** MAC (computed by another party) - is valid. - - Args: - mac_tag (byte string/byte string/memoryview): the expected MAC of the message. - - Raises: - ValueError: if the MAC does not match. It means that the message - has been tampered with or that the MAC key is incorrect. - """ - - secret = get_random_bytes(16) - - mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=mac_tag) - mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=self.digest()) - - if mac1.digest() != mac2.digest(): - raise ValueError("MAC check failed") - - def hexdigest(self): - """Return the **printable** MAC tag of the message authenticated so far. - - :return: The MAC tag, computed over the data processed so far. - Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) - for x in tuple(self.digest())]) - - def hexverify(self, hex_mac_tag): - """Verify that a given **printable** MAC (computed by another party) - is valid. - - Args: - hex_mac_tag (string): the expected MAC of the message, - as a hexadecimal string. - - Raises: - ValueError: if the MAC does not match. It means that the message - has been tampered with or that the MAC key is incorrect. - """ - - self.verify(unhexlify(tobytes(hex_mac_tag))) - - -def new(key, msg=b"", digestmod=None): - """Create a new MAC object. - - Args: - key (bytes/bytearray/memoryview): - key for the MAC object. - It must be long enough to match the expected security level of the - MAC. - msg (bytes/bytearray/memoryview): - Optional. The very first chunk of the message to authenticate. - It is equivalent to an early call to :meth:`HMAC.update`. - digestmod (module): - The hash to use to implement the HMAC. - Default is :mod:`Cryptodome.Hash.MD5`. - - Returns: - An :class:`HMAC` object - """ - - return HMAC(key, msg, digestmod) diff --git a/resources/lib/deps/Cryptodome/Hash/HMAC.pyi b/resources/lib/deps/Cryptodome/Hash/HMAC.pyi deleted file mode 100644 index b577230..0000000 --- a/resources/lib/deps/Cryptodome/Hash/HMAC.pyi +++ /dev/null @@ -1,25 +0,0 @@ -from types import ModuleType -from typing import Union, Dict - -Buffer = Union[bytes, bytearray, memoryview] - -digest_size: int - -class HMAC(object): - digest_size: int - - def __init__(self, - key: Buffer, - msg: Buffer, - digestmod: ModuleType) -> None: ... - def update(self, msg: Buffer) -> HMAC: ... - def copy(self) -> HMAC: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def verify(self, mac_tag: Buffer) -> None: ... - def hexverify(self, hex_mac_tag: str) -> None: ... - - -def new(key: Buffer, - msg: Buffer = ..., - digestmod: ModuleType = ...) -> HMAC: ... diff --git a/resources/lib/deps/Cryptodome/Hash/KMAC128.py b/resources/lib/deps/Cryptodome/Hash/KMAC128.py deleted file mode 100644 index afd91c4..0000000 --- a/resources/lib/deps/Cryptodome/Hash/KMAC128.py +++ /dev/null @@ -1,179 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2021, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from binascii import unhexlify - -from Cryptodome.Util.py3compat import bord, tobytes, is_bytes -from Cryptodome.Random import get_random_bytes - -from . import cSHAKE128, SHA3_256 -from .cSHAKE128 import _bytepad, _encode_str, _right_encode - - -class KMAC_Hash(object): - """A KMAC hash object. - Do not instantiate directly. - Use the :func:`new` function. - """ - - def __init__(self, data, key, mac_len, custom, - oid_variant, cshake, rate): - - # See https://tools.ietf.org/html/rfc8702 - self.oid = "2.16.840.1.101.3.4.2." + oid_variant - self.digest_size = mac_len - - self._mac = None - - partial_newX = _bytepad(_encode_str(tobytes(key)), rate) - self._cshake = cshake._new(partial_newX, custom, b"KMAC") - - if data: - self._cshake.update(data) - - def update(self, data): - """Authenticate the next chunk of message. - - Args: - data (bytes/bytearray/memoryview): The next chunk of the message to - authenticate. - """ - - if self._mac: - raise TypeError("You can only call 'digest' or 'hexdigest' on this object") - - self._cshake.update(data) - return self - - def digest(self): - """Return the **binary** (non-printable) MAC tag of the message. - - :return: The MAC tag. Binary form. - :rtype: byte string - """ - - if not self._mac: - self._cshake.update(_right_encode(self.digest_size * 8)) - self._mac = self._cshake.read(self.digest_size) - - return self._mac - - def hexdigest(self): - """Return the **printable** MAC tag of the message. - - :return: The MAC tag. Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) for x in tuple(self.digest())]) - - def verify(self, mac_tag): - """Verify that a given **binary** MAC (computed by another party) - is valid. - - Args: - mac_tag (bytes/bytearray/memoryview): the expected MAC of the message. - - Raises: - ValueError: if the MAC does not match. It means that the message - has been tampered with or that the MAC key is incorrect. - """ - - secret = get_random_bytes(16) - - mac1 = SHA3_256.new(secret + mac_tag) - mac2 = SHA3_256.new(secret + self.digest()) - - if mac1.digest() != mac2.digest(): - raise ValueError("MAC check failed") - - def hexverify(self, hex_mac_tag): - """Verify that a given **printable** MAC (computed by another party) - is valid. - - Args: - hex_mac_tag (string): the expected MAC of the message, as a hexadecimal string. - - Raises: - ValueError: if the MAC does not match. It means that the message - has been tampered with or that the MAC key is incorrect. - """ - - self.verify(unhexlify(tobytes(hex_mac_tag))) - - def new(self, **kwargs): - """Return a new instance of a KMAC hash object. - See :func:`new`. - """ - - if "mac_len" not in kwargs: - kwargs["mac_len"] = self.digest_size - - return new(**kwargs) - - -def new(**kwargs): - """Create a new KMAC128 object. - - Args: - key (bytes/bytearray/memoryview): - The key to use to compute the MAC. - It must be at least 128 bits long (16 bytes). - data (bytes/bytearray/memoryview): - Optional. The very first chunk of the message to authenticate. - It is equivalent to an early call to :meth:`KMAC_Hash.update`. - mac_len (integer): - Optional. The size of the authentication tag, in bytes. - Default is 64. Minimum is 8. - custom (bytes/bytearray/memoryview): - Optional. A customization byte string (``S`` in SP 800-185). - - Returns: - A :class:`KMAC_Hash` hash object - """ - - key = kwargs.pop("key", None) - if not is_bytes(key): - raise TypeError("You must pass a key to KMAC128") - if len(key) < 16: - raise ValueError("The key must be at least 128 bits long (16 bytes)") - - data = kwargs.pop("data", None) - - mac_len = kwargs.pop("mac_len", 64) - if mac_len < 8: - raise ValueError("'mac_len' must be 8 bytes or more") - - custom = kwargs.pop("custom", b"") - - if kwargs: - raise TypeError("Unknown parameters: " + str(kwargs)) - - return KMAC_Hash(data, key, mac_len, custom, "19", cSHAKE128, 168) diff --git a/resources/lib/deps/Cryptodome/Hash/KMAC128.pyi b/resources/lib/deps/Cryptodome/Hash/KMAC128.pyi deleted file mode 100644 index 8947dab..0000000 --- a/resources/lib/deps/Cryptodome/Hash/KMAC128.pyi +++ /dev/null @@ -1,33 +0,0 @@ -from typing import Union -from types import ModuleType - -Buffer = Union[bytes, bytearray, memoryview] - -class KMAC_Hash(object): - - def __init__(self, - data: Buffer, - key: Buffer, - mac_len: int, - custom: Buffer, - oid_variant: str, - cshake: ModuleType, - rate: int) -> None: ... - - def update(self, data: Buffer) -> KMAC_Hash: ... - - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def verify(self, mac_tag: Buffer) -> None: ... - def hexverify(self, hex_mac_tag: str) -> None: ... - def new(self, - data: Buffer = ..., - mac_len: int = ..., - key: Buffer = ..., - custom: Buffer = ...) -> KMAC_Hash: ... - - -def new(key: Buffer, - data: Buffer = ..., - mac_len: int = ..., - custom: Buffer = ...) -> KMAC_Hash: ... diff --git a/resources/lib/deps/Cryptodome/Hash/KMAC256.py b/resources/lib/deps/Cryptodome/Hash/KMAC256.py deleted file mode 100644 index 82da062..0000000 --- a/resources/lib/deps/Cryptodome/Hash/KMAC256.py +++ /dev/null @@ -1,74 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2021, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from Cryptodome.Util.py3compat import is_bytes - -from .KMAC128 import KMAC_Hash -from . import cSHAKE256 - - -def new(**kwargs): - """Create a new KMAC256 object. - - Args: - key (bytes/bytearray/memoryview): - The key to use to compute the MAC. - It must be at least 256 bits long (32 bytes). - data (bytes/bytearray/memoryview): - Optional. The very first chunk of the message to authenticate. - It is equivalent to an early call to :meth:`KMAC_Hash.update`. - mac_len (integer): - Optional. The size of the authentication tag, in bytes. - Default is 64. Minimum is 8. - custom (bytes/bytearray/memoryview): - Optional. A customization byte string (``S`` in SP 800-185). - - Returns: - A :class:`KMAC_Hash` hash object - """ - - key = kwargs.pop("key", None) - if not is_bytes(key): - raise TypeError("You must pass a key to KMAC256") - if len(key) < 32: - raise ValueError("The key must be at least 256 bits long (32 bytes)") - - data = kwargs.pop("data", None) - - mac_len = kwargs.pop("mac_len", 64) - if mac_len < 8: - raise ValueError("'mac_len' must be 8 bytes or more") - - custom = kwargs.pop("custom", b"") - - if kwargs: - raise TypeError("Unknown parameters: " + str(kwargs)) - - return KMAC_Hash(data, key, mac_len, custom, "20", cSHAKE256, 136) diff --git a/resources/lib/deps/Cryptodome/Hash/KMAC256.pyi b/resources/lib/deps/Cryptodome/Hash/KMAC256.pyi deleted file mode 100644 index 86cc500..0000000 --- a/resources/lib/deps/Cryptodome/Hash/KMAC256.pyi +++ /dev/null @@ -1,10 +0,0 @@ -from typing import Union - -from .KMAC128 import KMAC_Hash - -Buffer = Union[bytes, bytearray, memoryview] - -def new(key: Buffer, - data: Buffer = ..., - mac_len: int = ..., - custom: Buffer = ...) -> KMAC_Hash: ... diff --git a/resources/lib/deps/Cryptodome/Hash/KangarooTwelve.py b/resources/lib/deps/Cryptodome/Hash/KangarooTwelve.py deleted file mode 100644 index 60ced57..0000000 --- a/resources/lib/deps/Cryptodome/Hash/KangarooTwelve.py +++ /dev/null @@ -1,222 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2021, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from Cryptodome.Util.number import long_to_bytes -from Cryptodome.Util.py3compat import bchr - -from . import TurboSHAKE128 - -def _length_encode(x): - if x == 0: - return b'\x00' - - S = long_to_bytes(x) - return S + bchr(len(S)) - - -# Possible states for a KangarooTwelve instance, which depend on the amount of data processed so far. -SHORT_MSG = 1 # Still within the first 8192 bytes, but it is not certain we will exceed them. -LONG_MSG_S0 = 2 # Still within the first 8192 bytes, and it is certain we will exceed them. -LONG_MSG_SX = 3 # Beyond the first 8192 bytes. -SQUEEZING = 4 # No more data to process. - - -class K12_XOF(object): - """A KangarooTwelve hash object. - Do not instantiate directly. - Use the :func:`new` function. - """ - - def __init__(self, data, custom): - - if custom == None: - custom = b'' - - self._custom = custom + _length_encode(len(custom)) - self._state = SHORT_MSG - self._padding = None # Final padding is only decided in read() - - # Internal hash that consumes FinalNode - # The real domain separation byte will be known before squeezing - self._hash1 = TurboSHAKE128.new(domain=1) - self._length1 = 0 - - # Internal hash that produces CV_i (reset each time) - self._hash2 = None - self._length2 = 0 - - # Incremented by one for each 8192-byte block - self._ctr = 0 - - if data: - self.update(data) - - def update(self, data): - """Hash the next piece of data. - - .. note:: - For better performance, submit chunks with a length multiple of 8192 bytes. - - Args: - data (byte string/byte array/memoryview): The next chunk of the - message to hash. - """ - - if self._state == SQUEEZING: - raise TypeError("You cannot call 'update' after the first 'read'") - - if self._state == SHORT_MSG: - next_length = self._length1 + len(data) - - if next_length + len(self._custom) <= 8192: - self._length1 = next_length - self._hash1.update(data) - return self - - # Switch to tree hashing - self._state = LONG_MSG_S0 - - if self._state == LONG_MSG_S0: - data_mem = memoryview(data) - assert(self._length1 < 8192) - dtc = min(len(data), 8192 - self._length1) - self._hash1.update(data_mem[:dtc]) - self._length1 += dtc - - if self._length1 < 8192: - return self - - # Finish hashing S_0 and start S_1 - assert(self._length1 == 8192) - - divider = b'\x03' + b'\x00' * 7 - self._hash1.update(divider) - self._length1 += 8 - - self._hash2 = TurboSHAKE128.new(domain=0x0B) - self._length2 = 0 - self._ctr = 1 - - self._state = LONG_MSG_SX - return self.update(data_mem[dtc:]) - - # LONG_MSG_SX - assert(self._state == LONG_MSG_SX) - index = 0 - len_data = len(data) - - # All iteractions could actually run in parallel - data_mem = memoryview(data) - while index < len_data: - - new_index = min(index + 8192 - self._length2, len_data) - self._hash2.update(data_mem[index:new_index]) - self._length2 += new_index - index - index = new_index - - if self._length2 == 8192: - cv_i = self._hash2.read(32) - self._hash1.update(cv_i) - self._length1 += 32 - self._hash2._reset() - self._length2 = 0 - self._ctr += 1 - - return self - - def read(self, length): - """ - Produce more bytes of the digest. - - .. note:: - You cannot use :meth:`update` anymore after the first call to - :meth:`read`. - - Args: - length (integer): the amount of bytes this method must return - - :return: the next piece of XOF output (of the given length) - :rtype: byte string - """ - - custom_was_consumed = False - - if self._state == SHORT_MSG: - self._hash1.update(self._custom) - self._padding = 0x07 - self._state = SQUEEZING - - if self._state == LONG_MSG_S0: - self.update(self._custom) - custom_was_consumed = True - assert(self._state == LONG_MSG_SX) - - if self._state == LONG_MSG_SX: - if not custom_was_consumed: - self.update(self._custom) - - # Is there still some leftover data in hash2? - if self._length2 > 0: - cv_i = self._hash2.read(32) - self._hash1.update(cv_i) - self._length1 += 32 - self._hash2._reset() - self._length2 = 0 - self._ctr += 1 - - trailer = _length_encode(self._ctr - 1) + b'\xFF\xFF' - self._hash1.update(trailer) - - self._padding = 0x06 - self._state = SQUEEZING - - self._hash1._domain = self._padding - return self._hash1.read(length) - - def new(self, data=None, custom=b''): - return type(self)(data, custom) - - -def new(data=None, custom=None): - """Return a fresh instance of a KangarooTwelve object. - - Args: - data (bytes/bytearray/memoryview): - Optional. - The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`update`. - custom (bytes): - Optional. - A customization byte string. - - :Return: A :class:`K12_XOF` object - """ - - return K12_XOF(data, custom) diff --git a/resources/lib/deps/Cryptodome/Hash/KangarooTwelve.pyi b/resources/lib/deps/Cryptodome/Hash/KangarooTwelve.pyi deleted file mode 100644 index 8b3fd74..0000000 --- a/resources/lib/deps/Cryptodome/Hash/KangarooTwelve.pyi +++ /dev/null @@ -1,16 +0,0 @@ -from typing import Union, Optional - -Buffer = Union[bytes, bytearray, memoryview] - -class K12_XOF(object): - def __init__(self, - data: Optional[Buffer] = ..., - custom: Optional[bytes] = ...) -> None: ... - def update(self, data: Buffer) -> K12_XOF: ... - def read(self, length: int) -> bytes: ... - def new(self, - data: Optional[Buffer] = ..., - custom: Optional[bytes] = ...) -> None: ... - -def new(data: Optional[Buffer] = ..., - custom: Optional[Buffer] = ...) -> K12_XOF: ... diff --git a/resources/lib/deps/Cryptodome/Hash/MD2.py b/resources/lib/deps/Cryptodome/Hash/MD2.py deleted file mode 100644 index 47ecc05..0000000 --- a/resources/lib/deps/Cryptodome/Hash/MD2.py +++ /dev/null @@ -1,166 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from Cryptodome.Util.py3compat import bord - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr) - -_raw_md2_lib = load_pycryptodome_raw_lib( - "Cryptodome.Hash._MD2", - """ - int md2_init(void **shaState); - int md2_destroy(void *shaState); - int md2_update(void *hs, - const uint8_t *buf, - size_t len); - int md2_digest(const void *shaState, - uint8_t digest[20]); - int md2_copy(const void *src, void *dst); - """) - - -class MD2Hash(object): - """An MD2 hash object. - Do not instantiate directly. Use the :func:`new` function. - - :ivar oid: ASN.1 Object ID - :vartype oid: string - - :ivar block_size: the size in bytes of the internal message block, - input to the compression function - :vartype block_size: integer - - :ivar digest_size: the size in bytes of the resulting hash - :vartype digest_size: integer - """ - - # The size of the resulting hash in bytes. - digest_size = 16 - # The internal block size of the hash algorithm in bytes. - block_size = 16 - # ASN.1 Object ID - oid = "1.2.840.113549.2.2" - - def __init__(self, data=None): - state = VoidPointer() - result = _raw_md2_lib.md2_init(state.address_of()) - if result: - raise ValueError("Error %d while instantiating MD2" - % result) - self._state = SmartPointer(state.get(), - _raw_md2_lib.md2_destroy) - if data: - self.update(data) - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Args: - data (byte string/byte array/memoryview): The next chunk of the message being hashed. - """ - - result = _raw_md2_lib.md2_update(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data))) - if result: - raise ValueError("Error %d while instantiating MD2" - % result) - - def digest(self): - """Return the **binary** (non-printable) digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Binary form. - :rtype: byte string - """ - - bfr = create_string_buffer(self.digest_size) - result = _raw_md2_lib.md2_digest(self._state.get(), - bfr) - if result: - raise ValueError("Error %d while instantiating MD2" - % result) - - return get_raw_buffer(bfr) - - def hexdigest(self): - """Return the **printable** digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def copy(self): - """Return a copy ("clone") of the hash object. - - The copy will have the same internal state as the original hash - object. - This can be used to efficiently compute the digests of strings that - share a common initial substring. - - :return: A hash object of the same type - """ - - clone = MD2Hash() - result = _raw_md2_lib.md2_copy(self._state.get(), - clone._state.get()) - if result: - raise ValueError("Error %d while copying MD2" % result) - return clone - - def new(self, data=None): - return MD2Hash(data) - - -def new(data=None): - """Create a new hash object. - - :parameter data: - Optional. The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`MD2Hash.update`. - :type data: bytes/bytearray/memoryview - - :Return: A :class:`MD2Hash` hash object - """ - - return MD2Hash().new(data) - -# The size of the resulting hash in bytes. -digest_size = MD2Hash.digest_size - -# The internal block size of the hash algorithm in bytes. -block_size = MD2Hash.block_size diff --git a/resources/lib/deps/Cryptodome/Hash/MD2.pyi b/resources/lib/deps/Cryptodome/Hash/MD2.pyi deleted file mode 100644 index 95a97a9..0000000 --- a/resources/lib/deps/Cryptodome/Hash/MD2.pyi +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Union - -Buffer = Union[bytes, bytearray, memoryview] - -class MD4Hash(object): - digest_size: int - block_size: int - oid: str - - def __init__(self, data: Buffer = ...) -> None: ... - def update(self, data: Buffer) -> None: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def copy(self) -> MD4Hash: ... - def new(self, data: Buffer = ...) -> MD4Hash: ... - -def new(data: Buffer = ...) -> MD4Hash: ... -digest_size: int -block_size: int diff --git a/resources/lib/deps/Cryptodome/Hash/MD4.py b/resources/lib/deps/Cryptodome/Hash/MD4.py deleted file mode 100644 index 668fa65..0000000 --- a/resources/lib/deps/Cryptodome/Hash/MD4.py +++ /dev/null @@ -1,185 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -""" -MD4 is specified in RFC1320_ and produces the 128 bit digest of a message. - - >>> from Cryptodome.Hash import MD4 - >>> - >>> h = MD4.new() - >>> h.update(b'Hello') - >>> print h.hexdigest() - -MD4 stand for Message Digest version 4, and it was invented by Rivest in 1990. -This algorithm is insecure. Do not use it for new designs. - -.. _RFC1320: http://tools.ietf.org/html/rfc1320 -""" - -from Cryptodome.Util.py3compat import bord - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr) - -_raw_md4_lib = load_pycryptodome_raw_lib( - "Cryptodome.Hash._MD4", - """ - int md4_init(void **shaState); - int md4_destroy(void *shaState); - int md4_update(void *hs, - const uint8_t *buf, - size_t len); - int md4_digest(const void *shaState, - uint8_t digest[20]); - int md4_copy(const void *src, void *dst); - """) - - -class MD4Hash(object): - """Class that implements an MD4 hash - """ - - #: The size of the resulting hash in bytes. - digest_size = 16 - #: The internal block size of the hash algorithm in bytes. - block_size = 64 - #: ASN.1 Object ID - oid = "1.2.840.113549.2.4" - - def __init__(self, data=None): - state = VoidPointer() - result = _raw_md4_lib.md4_init(state.address_of()) - if result: - raise ValueError("Error %d while instantiating MD4" - % result) - self._state = SmartPointer(state.get(), - _raw_md4_lib.md4_destroy) - if data: - self.update(data) - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Repeated calls are equivalent to a single call with the concatenation - of all the arguments. In other words: - - >>> m.update(a); m.update(b) - - is equivalent to: - - >>> m.update(a+b) - - :Parameters: - data : byte string/byte array/memoryview - The next chunk of the message being hashed. - """ - - result = _raw_md4_lib.md4_update(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data))) - if result: - raise ValueError("Error %d while instantiating MD4" - % result) - - def digest(self): - """Return the **binary** (non-printable) digest of the message that - has been hashed so far. - - This method does not change the state of the hash object. - You can continue updating the object after calling this function. - - :Return: A byte string of `digest_size` bytes. It may contain non-ASCII - characters, including null bytes. - """ - - bfr = create_string_buffer(self.digest_size) - result = _raw_md4_lib.md4_digest(self._state.get(), - bfr) - if result: - raise ValueError("Error %d while instantiating MD4" - % result) - - return get_raw_buffer(bfr) - - def hexdigest(self): - """Return the **printable** digest of the message that has been - hashed so far. - - This method does not change the state of the hash object. - - :Return: A string of 2* `digest_size` characters. It contains only - hexadecimal ASCII digits. - """ - - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def copy(self): - """Return a copy ("clone") of the hash object. - - The copy will have the same internal state as the original hash - object. - This can be used to efficiently compute the digests of strings that - share a common initial substring. - - :Return: A hash object of the same type - """ - - clone = MD4Hash() - result = _raw_md4_lib.md4_copy(self._state.get(), - clone._state.get()) - if result: - raise ValueError("Error %d while copying MD4" % result) - return clone - - def new(self, data=None): - return MD4Hash(data) - - -def new(data=None): - """Return a fresh instance of the hash object. - - :Parameters: - data : byte string/byte array/memoryview - The very first chunk of the message to hash. - It is equivalent to an early call to `MD4Hash.update()`. - Optional. - - :Return: A `MD4Hash` object - """ - return MD4Hash().new(data) - -#: The size of the resulting hash in bytes. -digest_size = MD4Hash.digest_size - -#: The internal block size of the hash algorithm in bytes. -block_size = MD4Hash.block_size diff --git a/resources/lib/deps/Cryptodome/Hash/MD4.pyi b/resources/lib/deps/Cryptodome/Hash/MD4.pyi deleted file mode 100644 index a9a7295..0000000 --- a/resources/lib/deps/Cryptodome/Hash/MD4.pyi +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Union, Optional - -Buffer = Union[bytes, bytearray, memoryview] - -class MD4Hash(object): - digest_size: int - block_size: int - oid: str - - def __init__(self, data: Optional[Buffer] = ...) -> None: ... - def update(self, data: Buffer) -> None: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def copy(self) -> MD4Hash: ... - def new(self, data: Optional[Buffer] = ...) -> MD4Hash: ... - -def new(data: Optional[Buffer] = ...) -> MD4Hash: ... -digest_size: int -block_size: int diff --git a/resources/lib/deps/Cryptodome/Hash/MD5.py b/resources/lib/deps/Cryptodome/Hash/MD5.py deleted file mode 100644 index 8f573a9..0000000 --- a/resources/lib/deps/Cryptodome/Hash/MD5.py +++ /dev/null @@ -1,184 +0,0 @@ -# -*- coding: utf-8 -*- -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -from Cryptodome.Util.py3compat import * - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr) - -_raw_md5_lib = load_pycryptodome_raw_lib("Cryptodome.Hash._MD5", - """ - #define MD5_DIGEST_SIZE 16 - - int MD5_init(void **shaState); - int MD5_destroy(void *shaState); - int MD5_update(void *hs, - const uint8_t *buf, - size_t len); - int MD5_digest(const void *shaState, - uint8_t digest[MD5_DIGEST_SIZE]); - int MD5_copy(const void *src, void *dst); - - int MD5_pbkdf2_hmac_assist(const void *inner, - const void *outer, - const uint8_t first_digest[MD5_DIGEST_SIZE], - uint8_t final_digest[MD5_DIGEST_SIZE], - size_t iterations); - """) - -class MD5Hash(object): - """A MD5 hash object. - Do not instantiate directly. - Use the :func:`new` function. - - :ivar oid: ASN.1 Object ID - :vartype oid: string - - :ivar block_size: the size in bytes of the internal message block, - input to the compression function - :vartype block_size: integer - - :ivar digest_size: the size in bytes of the resulting hash - :vartype digest_size: integer - """ - - # The size of the resulting hash in bytes. - digest_size = 16 - # The internal block size of the hash algorithm in bytes. - block_size = 64 - # ASN.1 Object ID - oid = "1.2.840.113549.2.5" - - def __init__(self, data=None): - state = VoidPointer() - result = _raw_md5_lib.MD5_init(state.address_of()) - if result: - raise ValueError("Error %d while instantiating MD5" - % result) - self._state = SmartPointer(state.get(), - _raw_md5_lib.MD5_destroy) - if data: - self.update(data) - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Args: - data (byte string/byte array/memoryview): The next chunk of the message being hashed. - """ - - result = _raw_md5_lib.MD5_update(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data))) - if result: - raise ValueError("Error %d while instantiating MD5" - % result) - - def digest(self): - """Return the **binary** (non-printable) digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Binary form. - :rtype: byte string - """ - - bfr = create_string_buffer(self.digest_size) - result = _raw_md5_lib.MD5_digest(self._state.get(), - bfr) - if result: - raise ValueError("Error %d while instantiating MD5" - % result) - - return get_raw_buffer(bfr) - - def hexdigest(self): - """Return the **printable** digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def copy(self): - """Return a copy ("clone") of the hash object. - - The copy will have the same internal state as the original hash - object. - This can be used to efficiently compute the digests of strings that - share a common initial substring. - - :return: A hash object of the same type - """ - - clone = MD5Hash() - result = _raw_md5_lib.MD5_copy(self._state.get(), - clone._state.get()) - if result: - raise ValueError("Error %d while copying MD5" % result) - return clone - - def new(self, data=None): - """Create a fresh SHA-1 hash object.""" - - return MD5Hash(data) - - -def new(data=None): - """Create a new hash object. - - :parameter data: - Optional. The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`MD5Hash.update`. - :type data: byte string/byte array/memoryview - - :Return: A :class:`MD5Hash` hash object - """ - return MD5Hash().new(data) - -# The size of the resulting hash in bytes. -digest_size = 16 - -# The internal block size of the hash algorithm in bytes. -block_size = 64 - - -def _pbkdf2_hmac_assist(inner, outer, first_digest, iterations): - """Compute the expensive inner loop in PBKDF-HMAC.""" - - assert len(first_digest) == digest_size - assert iterations > 0 - - bfr = create_string_buffer(digest_size); - result = _raw_md5_lib.MD5_pbkdf2_hmac_assist( - inner._state.get(), - outer._state.get(), - first_digest, - bfr, - c_size_t(iterations)) - - if result: - raise ValueError("Error %d with PBKDF2-HMAC assis for MD5" % result) - - return get_raw_buffer(bfr) diff --git a/resources/lib/deps/Cryptodome/Hash/MD5.pyi b/resources/lib/deps/Cryptodome/Hash/MD5.pyi deleted file mode 100644 index d819556..0000000 --- a/resources/lib/deps/Cryptodome/Hash/MD5.pyi +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Union - -Buffer = Union[bytes, bytearray, memoryview] - -class MD5Hash(object): - digest_size: int - block_size: int - oid: str - - def __init__(self, data: Buffer = ...) -> None: ... - def update(self, data: Buffer) -> None: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def copy(self) -> MD5Hash: ... - def new(self, data: Buffer = ...) -> MD5Hash: ... - -def new(data: Buffer = ...) -> MD5Hash: ... -digest_size: int -block_size: int diff --git a/resources/lib/deps/Cryptodome/Hash/Poly1305.py b/resources/lib/deps/Cryptodome/Hash/Poly1305.py deleted file mode 100644 index c03f522..0000000 --- a/resources/lib/deps/Cryptodome/Hash/Poly1305.py +++ /dev/null @@ -1,217 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Hash/Poly1305.py - Implements the Poly1305 MAC -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -from binascii import unhexlify - -from Cryptodome.Util.py3compat import bord, tobytes, _copy_bytes - -from Cryptodome.Hash import BLAKE2s -from Cryptodome.Random import get_random_bytes -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr) - - -_raw_poly1305 = load_pycryptodome_raw_lib("Cryptodome.Hash._poly1305", - """ - int poly1305_init(void **state, - const uint8_t *r, - size_t r_len, - const uint8_t *s, - size_t s_len); - int poly1305_destroy(void *state); - int poly1305_update(void *state, - const uint8_t *in, - size_t len); - int poly1305_digest(const void *state, - uint8_t *digest, - size_t len); - """) - - -class Poly1305_MAC(object): - """An Poly1305 MAC object. - Do not instantiate directly. Use the :func:`new` function. - - :ivar digest_size: the size in bytes of the resulting MAC tag - :vartype digest_size: integer - """ - - digest_size = 16 - - def __init__(self, r, s, data): - - if len(r) != 16: - raise ValueError("Parameter r is not 16 bytes long") - if len(s) != 16: - raise ValueError("Parameter s is not 16 bytes long") - - self._mac_tag = None - - state = VoidPointer() - result = _raw_poly1305.poly1305_init(state.address_of(), - c_uint8_ptr(r), - c_size_t(len(r)), - c_uint8_ptr(s), - c_size_t(len(s)) - ) - if result: - raise ValueError("Error %d while instantiating Poly1305" % result) - self._state = SmartPointer(state.get(), - _raw_poly1305.poly1305_destroy) - if data: - self.update(data) - - def update(self, data): - """Authenticate the next chunk of message. - - Args: - data (byte string/byte array/memoryview): The next chunk of data - """ - - if self._mac_tag: - raise TypeError("You can only call 'digest' or 'hexdigest' on this object") - - result = _raw_poly1305.poly1305_update(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data))) - if result: - raise ValueError("Error %d while hashing Poly1305 data" % result) - return self - - def copy(self): - raise NotImplementedError() - - def digest(self): - """Return the **binary** (non-printable) MAC tag of the message - authenticated so far. - - :return: The MAC tag digest, computed over the data processed so far. - Binary form. - :rtype: byte string - """ - - if self._mac_tag: - return self._mac_tag - - bfr = create_string_buffer(16) - result = _raw_poly1305.poly1305_digest(self._state.get(), - bfr, - c_size_t(len(bfr))) - if result: - raise ValueError("Error %d while creating Poly1305 digest" % result) - - self._mac_tag = get_raw_buffer(bfr) - return self._mac_tag - - def hexdigest(self): - """Return the **printable** MAC tag of the message authenticated so far. - - :return: The MAC tag, computed over the data processed so far. - Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) - for x in tuple(self.digest())]) - - def verify(self, mac_tag): - """Verify that a given **binary** MAC (computed by another party) - is valid. - - Args: - mac_tag (byte string/byte string/memoryview): the expected MAC of the message. - - Raises: - ValueError: if the MAC does not match. It means that the message - has been tampered with or that the MAC key is incorrect. - """ - - secret = get_random_bytes(16) - - mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=mac_tag) - mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=self.digest()) - - if mac1.digest() != mac2.digest(): - raise ValueError("MAC check failed") - - def hexverify(self, hex_mac_tag): - """Verify that a given **printable** MAC (computed by another party) - is valid. - - Args: - hex_mac_tag (string): the expected MAC of the message, - as a hexadecimal string. - - Raises: - ValueError: if the MAC does not match. It means that the message - has been tampered with or that the MAC key is incorrect. - """ - - self.verify(unhexlify(tobytes(hex_mac_tag))) - - - -def new(**kwargs): - """Create a new Poly1305 MAC object. - - Args: - key (bytes/bytearray/memoryview): - The 32-byte key for the Poly1305 object. - cipher (module from ``Cryptodome.Cipher``): - The cipher algorithm to use for deriving the Poly1305 - key pair *(r, s)*. - It can only be ``Cryptodome.Cipher.AES`` or ``Cryptodome.Cipher.ChaCha20``. - nonce (bytes/bytearray/memoryview): - Optional. The non-repeatable value to use for the MAC of this message. - It must be 16 bytes long for ``AES`` and 8 or 12 bytes for ``ChaCha20``. - If not passed, a random nonce is created; you will find it in the - ``nonce`` attribute of the new object. - data (bytes/bytearray/memoryview): - Optional. The very first chunk of the message to authenticate. - It is equivalent to an early call to ``update()``. - - Returns: - A :class:`Poly1305_MAC` object - """ - - cipher = kwargs.pop("cipher", None) - if not hasattr(cipher, '_derive_Poly1305_key_pair'): - raise ValueError("Parameter 'cipher' must be AES or ChaCha20") - - cipher_key = kwargs.pop("key", None) - if cipher_key is None: - raise TypeError("You must pass a parameter 'key'") - - nonce = kwargs.pop("nonce", None) - data = kwargs.pop("data", None) - - if kwargs: - raise TypeError("Unknown parameters: " + str(kwargs)) - - r, s, nonce = cipher._derive_Poly1305_key_pair(cipher_key, nonce) - - new_mac = Poly1305_MAC(r, s, data) - new_mac.nonce = _copy_bytes(None, None, nonce) # nonce may still be just a memoryview - return new_mac diff --git a/resources/lib/deps/Cryptodome/Hash/Poly1305.pyi b/resources/lib/deps/Cryptodome/Hash/Poly1305.pyi deleted file mode 100644 index f97a14a..0000000 --- a/resources/lib/deps/Cryptodome/Hash/Poly1305.pyi +++ /dev/null @@ -1,24 +0,0 @@ -from types import ModuleType -from typing import Union - -Buffer = Union[bytes, bytearray, memoryview] - -class Poly1305_MAC(object): - block_size: int - digest_size: int - oid: str - - def __init__(self, - r : int, - s : int, - data : Buffer) -> None: ... - def update(self, data: Buffer) -> Poly1305_MAC: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def verify(self, mac_tag: Buffer) -> None: ... - def hexverify(self, hex_mac_tag: str) -> None: ... - -def new(key: Buffer, - cipher: ModuleType, - nonce: Buffer = ..., - data: Buffer = ...) -> Poly1305_MAC: ... diff --git a/resources/lib/deps/Cryptodome/Hash/RIPEMD.py b/resources/lib/deps/Cryptodome/Hash/RIPEMD.py deleted file mode 100644 index 35ad576..0000000 --- a/resources/lib/deps/Cryptodome/Hash/RIPEMD.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -# This file exists for backward compatibility with old code that refers to -# Cryptodome.Hash.RIPEMD - -"""Deprecated alias for `Cryptodome.Hash.RIPEMD160`""" - -from Cryptodome.Hash.RIPEMD160 import new, block_size, digest_size diff --git a/resources/lib/deps/Cryptodome/Hash/RIPEMD.pyi b/resources/lib/deps/Cryptodome/Hash/RIPEMD.pyi deleted file mode 100644 index cfb2252..0000000 --- a/resources/lib/deps/Cryptodome/Hash/RIPEMD.pyi +++ /dev/null @@ -1,3 +0,0 @@ -# This file exists for backward compatibility with old code that refers to -# Cryptodome.Hash.SHA - diff --git a/resources/lib/deps/Cryptodome/Hash/RIPEMD160.py b/resources/lib/deps/Cryptodome/Hash/RIPEMD160.py deleted file mode 100644 index f959027..0000000 --- a/resources/lib/deps/Cryptodome/Hash/RIPEMD160.py +++ /dev/null @@ -1,169 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from Cryptodome.Util.py3compat import bord - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr) - -_raw_ripemd160_lib = load_pycryptodome_raw_lib( - "Cryptodome.Hash._RIPEMD160", - """ - int ripemd160_init(void **shaState); - int ripemd160_destroy(void *shaState); - int ripemd160_update(void *hs, - const uint8_t *buf, - size_t len); - int ripemd160_digest(const void *shaState, - uint8_t digest[20]); - int ripemd160_copy(const void *src, void *dst); - """) - - -class RIPEMD160Hash(object): - """A RIPEMD-160 hash object. - Do not instantiate directly. - Use the :func:`new` function. - - :ivar oid: ASN.1 Object ID - :vartype oid: string - - :ivar block_size: the size in bytes of the internal message block, - input to the compression function - :vartype block_size: integer - - :ivar digest_size: the size in bytes of the resulting hash - :vartype digest_size: integer - """ - - # The size of the resulting hash in bytes. - digest_size = 20 - # The internal block size of the hash algorithm in bytes. - block_size = 64 - # ASN.1 Object ID - oid = "1.3.36.3.2.1" - - def __init__(self, data=None): - state = VoidPointer() - result = _raw_ripemd160_lib.ripemd160_init(state.address_of()) - if result: - raise ValueError("Error %d while instantiating RIPEMD160" - % result) - self._state = SmartPointer(state.get(), - _raw_ripemd160_lib.ripemd160_destroy) - if data: - self.update(data) - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Args: - data (byte string/byte array/memoryview): The next chunk of the message being hashed. - """ - - result = _raw_ripemd160_lib.ripemd160_update(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data))) - if result: - raise ValueError("Error %d while instantiating ripemd160" - % result) - - def digest(self): - """Return the **binary** (non-printable) digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Binary form. - :rtype: byte string - """ - - bfr = create_string_buffer(self.digest_size) - result = _raw_ripemd160_lib.ripemd160_digest(self._state.get(), - bfr) - if result: - raise ValueError("Error %d while instantiating ripemd160" - % result) - - return get_raw_buffer(bfr) - - def hexdigest(self): - """Return the **printable** digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def copy(self): - """Return a copy ("clone") of the hash object. - - The copy will have the same internal state as the original hash - object. - This can be used to efficiently compute the digests of strings that - share a common initial substring. - - :return: A hash object of the same type - """ - - clone = RIPEMD160Hash() - result = _raw_ripemd160_lib.ripemd160_copy(self._state.get(), - clone._state.get()) - if result: - raise ValueError("Error %d while copying ripemd160" % result) - return clone - - def new(self, data=None): - """Create a fresh RIPEMD-160 hash object.""" - - return RIPEMD160Hash(data) - - -def new(data=None): - """Create a new hash object. - - :parameter data: - Optional. The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`RIPEMD160Hash.update`. - :type data: byte string/byte array/memoryview - - :Return: A :class:`RIPEMD160Hash` hash object - """ - - return RIPEMD160Hash().new(data) - -# The size of the resulting hash in bytes. -digest_size = RIPEMD160Hash.digest_size - -# The internal block size of the hash algorithm in bytes. -block_size = RIPEMD160Hash.block_size diff --git a/resources/lib/deps/Cryptodome/Hash/RIPEMD160.pyi b/resources/lib/deps/Cryptodome/Hash/RIPEMD160.pyi deleted file mode 100644 index b619473..0000000 --- a/resources/lib/deps/Cryptodome/Hash/RIPEMD160.pyi +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Union - -Buffer = Union[bytes, bytearray, memoryview] - -class RIPEMD160Hash(object): - digest_size: int - block_size: int - oid: str - - def __init__(self, data: Buffer = ...) -> None: ... - def update(self, data: Buffer) -> None: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def copy(self) -> RIPEMD160Hash: ... - def new(self, data: Buffer = ...) -> RIPEMD160Hash: ... - -def new(data: Buffer = ...) -> RIPEMD160Hash: ... -digest_size: int -block_size: int diff --git a/resources/lib/deps/Cryptodome/Hash/SHA.py b/resources/lib/deps/Cryptodome/Hash/SHA.py deleted file mode 100644 index 95f8745..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -# This file exists for backward compatibility with old code that refers to -# Cryptodome.Hash.SHA - -from Cryptodome.Hash.SHA1 import __doc__, new, block_size, digest_size diff --git a/resources/lib/deps/Cryptodome/Hash/SHA.pyi b/resources/lib/deps/Cryptodome/Hash/SHA.pyi deleted file mode 100644 index 7d01a5f..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA.pyi +++ /dev/null @@ -1,4 +0,0 @@ -# This file exists for backward compatibility with old code that refers to -# Cryptodome.Hash.SHA - -from Cryptodome.Hash.SHA1 import __doc__, new, block_size, digest_size diff --git a/resources/lib/deps/Cryptodome/Hash/SHA1.py b/resources/lib/deps/Cryptodome/Hash/SHA1.py deleted file mode 100644 index dea51bc..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA1.py +++ /dev/null @@ -1,185 +0,0 @@ -# -*- coding: utf-8 -*- -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -from Cryptodome.Util.py3compat import * - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr) - -_raw_sha1_lib = load_pycryptodome_raw_lib("Cryptodome.Hash._SHA1", - """ - #define SHA1_DIGEST_SIZE 20 - - int SHA1_init(void **shaState); - int SHA1_destroy(void *shaState); - int SHA1_update(void *hs, - const uint8_t *buf, - size_t len); - int SHA1_digest(const void *shaState, - uint8_t digest[SHA1_DIGEST_SIZE]); - int SHA1_copy(const void *src, void *dst); - - int SHA1_pbkdf2_hmac_assist(const void *inner, - const void *outer, - const uint8_t first_digest[SHA1_DIGEST_SIZE], - uint8_t final_digest[SHA1_DIGEST_SIZE], - size_t iterations); - """) - -class SHA1Hash(object): - """A SHA-1 hash object. - Do not instantiate directly. - Use the :func:`new` function. - - :ivar oid: ASN.1 Object ID - :vartype oid: string - - :ivar block_size: the size in bytes of the internal message block, - input to the compression function - :vartype block_size: integer - - :ivar digest_size: the size in bytes of the resulting hash - :vartype digest_size: integer - """ - - # The size of the resulting hash in bytes. - digest_size = 20 - # The internal block size of the hash algorithm in bytes. - block_size = 64 - # ASN.1 Object ID - oid = "1.3.14.3.2.26" - - def __init__(self, data=None): - state = VoidPointer() - result = _raw_sha1_lib.SHA1_init(state.address_of()) - if result: - raise ValueError("Error %d while instantiating SHA1" - % result) - self._state = SmartPointer(state.get(), - _raw_sha1_lib.SHA1_destroy) - if data: - self.update(data) - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Args: - data (byte string/byte array/memoryview): The next chunk of the message being hashed. - """ - - result = _raw_sha1_lib.SHA1_update(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data))) - if result: - raise ValueError("Error %d while instantiating SHA1" - % result) - - def digest(self): - """Return the **binary** (non-printable) digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Binary form. - :rtype: byte string - """ - - bfr = create_string_buffer(self.digest_size) - result = _raw_sha1_lib.SHA1_digest(self._state.get(), - bfr) - if result: - raise ValueError("Error %d while instantiating SHA1" - % result) - - return get_raw_buffer(bfr) - - def hexdigest(self): - """Return the **printable** digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def copy(self): - """Return a copy ("clone") of the hash object. - - The copy will have the same internal state as the original hash - object. - This can be used to efficiently compute the digests of strings that - share a common initial substring. - - :return: A hash object of the same type - """ - - clone = SHA1Hash() - result = _raw_sha1_lib.SHA1_copy(self._state.get(), - clone._state.get()) - if result: - raise ValueError("Error %d while copying SHA1" % result) - return clone - - def new(self, data=None): - """Create a fresh SHA-1 hash object.""" - - return SHA1Hash(data) - - -def new(data=None): - """Create a new hash object. - - :parameter data: - Optional. The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`SHA1Hash.update`. - :type data: byte string/byte array/memoryview - - :Return: A :class:`SHA1Hash` hash object - """ - return SHA1Hash().new(data) - - -# The size of the resulting hash in bytes. -digest_size = SHA1Hash.digest_size - -# The internal block size of the hash algorithm in bytes. -block_size = SHA1Hash.block_size - - -def _pbkdf2_hmac_assist(inner, outer, first_digest, iterations): - """Compute the expensive inner loop in PBKDF-HMAC.""" - - assert len(first_digest) == digest_size - assert iterations > 0 - - bfr = create_string_buffer(digest_size); - result = _raw_sha1_lib.SHA1_pbkdf2_hmac_assist( - inner._state.get(), - outer._state.get(), - first_digest, - bfr, - c_size_t(iterations)) - - if result: - raise ValueError("Error %d with PBKDF2-HMAC assis for SHA1" % result) - - return get_raw_buffer(bfr) diff --git a/resources/lib/deps/Cryptodome/Hash/SHA1.pyi b/resources/lib/deps/Cryptodome/Hash/SHA1.pyi deleted file mode 100644 index d6c8e25..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA1.pyi +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Union, Optional - -Buffer = Union[bytes, bytearray, memoryview] - -class SHA1Hash(object): - digest_size: int - block_size: int - oid: str - - def __init__(self, data: Optional[Buffer] = ...) -> None: ... - def update(self, data: Buffer) -> None: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def copy(self) -> SHA1Hash: ... - def new(self, data: Optional[Buffer] = ...) -> SHA1Hash: ... - -def new(data: Optional[Buffer] = ...) -> SHA1Hash: ... -digest_size: int -block_size: int diff --git a/resources/lib/deps/Cryptodome/Hash/SHA224.py b/resources/lib/deps/Cryptodome/Hash/SHA224.py deleted file mode 100644 index fca7622..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA224.py +++ /dev/null @@ -1,186 +0,0 @@ -# -*- coding: utf-8 -*- -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -from Cryptodome.Util.py3compat import bord - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr) - -_raw_sha224_lib = load_pycryptodome_raw_lib("Cryptodome.Hash._SHA224", - """ - int SHA224_init(void **shaState); - int SHA224_destroy(void *shaState); - int SHA224_update(void *hs, - const uint8_t *buf, - size_t len); - int SHA224_digest(const void *shaState, - uint8_t *digest, - size_t digest_size); - int SHA224_copy(const void *src, void *dst); - - int SHA224_pbkdf2_hmac_assist(const void *inner, - const void *outer, - const uint8_t *first_digest, - uint8_t *final_digest, - size_t iterations, - size_t digest_size); - """) - -class SHA224Hash(object): - """A SHA-224 hash object. - Do not instantiate directly. - Use the :func:`new` function. - - :ivar oid: ASN.1 Object ID - :vartype oid: string - - :ivar block_size: the size in bytes of the internal message block, - input to the compression function - :vartype block_size: integer - - :ivar digest_size: the size in bytes of the resulting hash - :vartype digest_size: integer - """ - - # The size of the resulting hash in bytes. - digest_size = 28 - # The internal block size of the hash algorithm in bytes. - block_size = 64 - # ASN.1 Object ID - oid = '2.16.840.1.101.3.4.2.4' - - def __init__(self, data=None): - state = VoidPointer() - result = _raw_sha224_lib.SHA224_init(state.address_of()) - if result: - raise ValueError("Error %d while instantiating SHA224" - % result) - self._state = SmartPointer(state.get(), - _raw_sha224_lib.SHA224_destroy) - if data: - self.update(data) - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Args: - data (byte string/byte array/memoryview): The next chunk of the message being hashed. - """ - - result = _raw_sha224_lib.SHA224_update(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data))) - if result: - raise ValueError("Error %d while hashing data with SHA224" - % result) - - def digest(self): - """Return the **binary** (non-printable) digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Binary form. - :rtype: byte string - """ - - bfr = create_string_buffer(self.digest_size) - result = _raw_sha224_lib.SHA224_digest(self._state.get(), - bfr, - c_size_t(self.digest_size)) - if result: - raise ValueError("Error %d while making SHA224 digest" - % result) - - return get_raw_buffer(bfr) - - def hexdigest(self): - """Return the **printable** digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def copy(self): - """Return a copy ("clone") of the hash object. - - The copy will have the same internal state as the original hash - object. - This can be used to efficiently compute the digests of strings that - share a common initial substring. - - :return: A hash object of the same type - """ - - clone = SHA224Hash() - result = _raw_sha224_lib.SHA224_copy(self._state.get(), - clone._state.get()) - if result: - raise ValueError("Error %d while copying SHA224" % result) - return clone - - def new(self, data=None): - """Create a fresh SHA-224 hash object.""" - - return SHA224Hash(data) - - -def new(data=None): - """Create a new hash object. - - :parameter data: - Optional. The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`SHA224Hash.update`. - :type data: byte string/byte array/memoryview - - :Return: A :class:`SHA224Hash` hash object - """ - return SHA224Hash().new(data) - - -# The size of the resulting hash in bytes. -digest_size = SHA224Hash.digest_size - -# The internal block size of the hash algorithm in bytes. -block_size = SHA224Hash.block_size - - -def _pbkdf2_hmac_assist(inner, outer, first_digest, iterations): - """Compute the expensive inner loop in PBKDF-HMAC.""" - - assert iterations > 0 - - bfr = create_string_buffer(len(first_digest)); - result = _raw_sha224_lib.SHA224_pbkdf2_hmac_assist( - inner._state.get(), - outer._state.get(), - first_digest, - bfr, - c_size_t(iterations), - c_size_t(len(first_digest))) - - if result: - raise ValueError("Error %d with PBKDF2-HMAC assist for SHA224" % result) - - return get_raw_buffer(bfr) diff --git a/resources/lib/deps/Cryptodome/Hash/SHA224.pyi b/resources/lib/deps/Cryptodome/Hash/SHA224.pyi deleted file mode 100644 index 613a7f9..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA224.pyi +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Union, Optional - -Buffer = Union[bytes, bytearray, memoryview] - -class SHA224Hash(object): - digest_size: int - block_size: int - oid: str - - def __init__(self, data: Optional[Buffer] = ...) -> None: ... - def update(self, data: Buffer) -> None: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def copy(self) -> SHA224Hash: ... - def new(self, data: Optional[Buffer] = ...) -> SHA224Hash: ... - -def new(data: Optional[Buffer] = ...) -> SHA224Hash: ... -digest_size: int -block_size: int diff --git a/resources/lib/deps/Cryptodome/Hash/SHA256.py b/resources/lib/deps/Cryptodome/Hash/SHA256.py deleted file mode 100644 index c1a81b1..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA256.py +++ /dev/null @@ -1,185 +0,0 @@ -# -*- coding: utf-8 -*- -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -from Cryptodome.Util.py3compat import bord - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr) - -_raw_sha256_lib = load_pycryptodome_raw_lib("Cryptodome.Hash._SHA256", - """ - int SHA256_init(void **shaState); - int SHA256_destroy(void *shaState); - int SHA256_update(void *hs, - const uint8_t *buf, - size_t len); - int SHA256_digest(const void *shaState, - uint8_t *digest, - size_t digest_size); - int SHA256_copy(const void *src, void *dst); - - int SHA256_pbkdf2_hmac_assist(const void *inner, - const void *outer, - const uint8_t *first_digest, - uint8_t *final_digest, - size_t iterations, - size_t digest_size); - """) - -class SHA256Hash(object): - """A SHA-256 hash object. - Do not instantiate directly. Use the :func:`new` function. - - :ivar oid: ASN.1 Object ID - :vartype oid: string - - :ivar block_size: the size in bytes of the internal message block, - input to the compression function - :vartype block_size: integer - - :ivar digest_size: the size in bytes of the resulting hash - :vartype digest_size: integer - """ - - # The size of the resulting hash in bytes. - digest_size = 32 - # The internal block size of the hash algorithm in bytes. - block_size = 64 - # ASN.1 Object ID - oid = "2.16.840.1.101.3.4.2.1" - - def __init__(self, data=None): - state = VoidPointer() - result = _raw_sha256_lib.SHA256_init(state.address_of()) - if result: - raise ValueError("Error %d while instantiating SHA256" - % result) - self._state = SmartPointer(state.get(), - _raw_sha256_lib.SHA256_destroy) - if data: - self.update(data) - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Args: - data (byte string/byte array/memoryview): The next chunk of the message being hashed. - """ - - result = _raw_sha256_lib.SHA256_update(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data))) - if result: - raise ValueError("Error %d while hashing data with SHA256" - % result) - - def digest(self): - """Return the **binary** (non-printable) digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Binary form. - :rtype: byte string - """ - - bfr = create_string_buffer(self.digest_size) - result = _raw_sha256_lib.SHA256_digest(self._state.get(), - bfr, - c_size_t(self.digest_size)) - if result: - raise ValueError("Error %d while making SHA256 digest" - % result) - - return get_raw_buffer(bfr) - - def hexdigest(self): - """Return the **printable** digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def copy(self): - """Return a copy ("clone") of the hash object. - - The copy will have the same internal state as the original hash - object. - This can be used to efficiently compute the digests of strings that - share a common initial substring. - - :return: A hash object of the same type - """ - - clone = SHA256Hash() - result = _raw_sha256_lib.SHA256_copy(self._state.get(), - clone._state.get()) - if result: - raise ValueError("Error %d while copying SHA256" % result) - return clone - - def new(self, data=None): - """Create a fresh SHA-256 hash object.""" - - return SHA256Hash(data) - -def new(data=None): - """Create a new hash object. - - :parameter data: - Optional. The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`SHA256Hash.update`. - :type data: byte string/byte array/memoryview - - :Return: A :class:`SHA256Hash` hash object - """ - - return SHA256Hash().new(data) - - -# The size of the resulting hash in bytes. -digest_size = SHA256Hash.digest_size - -# The internal block size of the hash algorithm in bytes. -block_size = SHA256Hash.block_size - - -def _pbkdf2_hmac_assist(inner, outer, first_digest, iterations): - """Compute the expensive inner loop in PBKDF-HMAC.""" - - assert iterations > 0 - - bfr = create_string_buffer(len(first_digest)); - result = _raw_sha256_lib.SHA256_pbkdf2_hmac_assist( - inner._state.get(), - outer._state.get(), - first_digest, - bfr, - c_size_t(iterations), - c_size_t(len(first_digest))) - - if result: - raise ValueError("Error %d with PBKDF2-HMAC assist for SHA256" % result) - - return get_raw_buffer(bfr) diff --git a/resources/lib/deps/Cryptodome/Hash/SHA256.pyi b/resources/lib/deps/Cryptodome/Hash/SHA256.pyi deleted file mode 100644 index cbf21bf..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA256.pyi +++ /dev/null @@ -1,18 +0,0 @@ -from typing import Union, Optional - - -class SHA256Hash(object): - digest_size: int - block_size: int - oid: str - def __init__(self, data: Optional[Union[bytes, bytearray, memoryview]]=None) -> None: ... - def update(self, data: Union[bytes, bytearray, memoryview]) -> None: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def copy(self) -> SHA256Hash: ... - def new(self, data: Optional[Union[bytes, bytearray, memoryview]]=None) -> SHA256Hash: ... - -def new(data: Optional[Union[bytes, bytearray, memoryview]]=None) -> SHA256Hash: ... - -digest_size: int -block_size: int diff --git a/resources/lib/deps/Cryptodome/Hash/SHA384.py b/resources/lib/deps/Cryptodome/Hash/SHA384.py deleted file mode 100644 index 711aa73..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA384.py +++ /dev/null @@ -1,186 +0,0 @@ -# -*- coding: utf-8 -*- -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -from Cryptodome.Util.py3compat import bord - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr) - -_raw_sha384_lib = load_pycryptodome_raw_lib("Cryptodome.Hash._SHA384", - """ - int SHA384_init(void **shaState); - int SHA384_destroy(void *shaState); - int SHA384_update(void *hs, - const uint8_t *buf, - size_t len); - int SHA384_digest(const void *shaState, - uint8_t *digest, - size_t digest_size); - int SHA384_copy(const void *src, void *dst); - - int SHA384_pbkdf2_hmac_assist(const void *inner, - const void *outer, - const uint8_t *first_digest, - uint8_t *final_digest, - size_t iterations, - size_t digest_size); - """) - -class SHA384Hash(object): - """A SHA-384 hash object. - Do not instantiate directly. Use the :func:`new` function. - - :ivar oid: ASN.1 Object ID - :vartype oid: string - - :ivar block_size: the size in bytes of the internal message block, - input to the compression function - :vartype block_size: integer - - :ivar digest_size: the size in bytes of the resulting hash - :vartype digest_size: integer - """ - - # The size of the resulting hash in bytes. - digest_size = 48 - # The internal block size of the hash algorithm in bytes. - block_size = 128 - # ASN.1 Object ID - oid = '2.16.840.1.101.3.4.2.2' - - def __init__(self, data=None): - state = VoidPointer() - result = _raw_sha384_lib.SHA384_init(state.address_of()) - if result: - raise ValueError("Error %d while instantiating SHA384" - % result) - self._state = SmartPointer(state.get(), - _raw_sha384_lib.SHA384_destroy) - if data: - self.update(data) - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Args: - data (byte string/byte array/memoryview): The next chunk of the message being hashed. - """ - - result = _raw_sha384_lib.SHA384_update(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data))) - if result: - raise ValueError("Error %d while hashing data with SHA384" - % result) - - def digest(self): - """Return the **binary** (non-printable) digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Binary form. - :rtype: byte string - """ - - bfr = create_string_buffer(self.digest_size) - result = _raw_sha384_lib.SHA384_digest(self._state.get(), - bfr, - c_size_t(self.digest_size)) - if result: - raise ValueError("Error %d while making SHA384 digest" - % result) - - return get_raw_buffer(bfr) - - def hexdigest(self): - """Return the **printable** digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def copy(self): - """Return a copy ("clone") of the hash object. - - The copy will have the same internal state as the original hash - object. - This can be used to efficiently compute the digests of strings that - share a common initial substring. - - :return: A hash object of the same type - """ - - clone = SHA384Hash() - result = _raw_sha384_lib.SHA384_copy(self._state.get(), - clone._state.get()) - if result: - raise ValueError("Error %d while copying SHA384" % result) - return clone - - def new(self, data=None): - """Create a fresh SHA-384 hash object.""" - - return SHA384Hash(data) - - -def new(data=None): - """Create a new hash object. - - :parameter data: - Optional. The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`SHA384Hash.update`. - :type data: byte string/byte array/memoryview - - :Return: A :class:`SHA384Hash` hash object - """ - - return SHA384Hash().new(data) - - -# The size of the resulting hash in bytes. -digest_size = SHA384Hash.digest_size - -# The internal block size of the hash algorithm in bytes. -block_size = SHA384Hash.block_size - - -def _pbkdf2_hmac_assist(inner, outer, first_digest, iterations): - """Compute the expensive inner loop in PBKDF-HMAC.""" - - assert iterations > 0 - - bfr = create_string_buffer(len(first_digest)); - result = _raw_sha384_lib.SHA384_pbkdf2_hmac_assist( - inner._state.get(), - outer._state.get(), - first_digest, - bfr, - c_size_t(iterations), - c_size_t(len(first_digest))) - - if result: - raise ValueError("Error %d with PBKDF2-HMAC assist for SHA384" % result) - - return get_raw_buffer(bfr) diff --git a/resources/lib/deps/Cryptodome/Hash/SHA384.pyi b/resources/lib/deps/Cryptodome/Hash/SHA384.pyi deleted file mode 100644 index c2aab9e..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA384.pyi +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Union, Optional - -Buffer = Union[bytes, bytearray, memoryview] - -class SHA384Hash(object): - digest_size: int - block_size: int - oid: str - - def __init__(self, data: Optional[Buffer] = ...) -> None: ... - def update(self, data: Buffer) -> None: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def copy(self) -> SHA384Hash: ... - def new(self, data: Optional[Buffer] = ...) -> SHA384Hash: ... - -def new(data: Optional[Buffer] = ...) -> SHA384Hash: ... -digest_size: int -block_size: int diff --git a/resources/lib/deps/Cryptodome/Hash/SHA3_224.py b/resources/lib/deps/Cryptodome/Hash/SHA3_224.py deleted file mode 100644 index 34888c5..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA3_224.py +++ /dev/null @@ -1,174 +0,0 @@ -# -*- coding: utf-8 -*- -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -from Cryptodome.Util.py3compat import bord - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr, c_ubyte) - -from Cryptodome.Hash.keccak import _raw_keccak_lib - -class SHA3_224_Hash(object): - """A SHA3-224 hash object. - Do not instantiate directly. - Use the :func:`new` function. - - :ivar oid: ASN.1 Object ID - :vartype oid: string - - :ivar digest_size: the size in bytes of the resulting hash - :vartype digest_size: integer - """ - - # The size of the resulting hash in bytes. - digest_size = 28 - - # ASN.1 Object ID - oid = "2.16.840.1.101.3.4.2.7" - - # Input block size for HMAC - block_size = 144 - - def __init__(self, data, update_after_digest): - self._update_after_digest = update_after_digest - self._digest_done = False - self._padding = 0x06 - - state = VoidPointer() - result = _raw_keccak_lib.keccak_init(state.address_of(), - c_size_t(self.digest_size * 2), - c_ubyte(24)) - if result: - raise ValueError("Error %d while instantiating SHA-3/224" - % result) - self._state = SmartPointer(state.get(), - _raw_keccak_lib.keccak_destroy) - if data: - self.update(data) - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Args: - data (byte string/byte array/memoryview): The next chunk of the message being hashed. - """ - - if self._digest_done and not self._update_after_digest: - raise TypeError("You can only call 'digest' or 'hexdigest' on this object") - - result = _raw_keccak_lib.keccak_absorb(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data)) - ) - if result: - raise ValueError("Error %d while updating SHA-3/224" - % result) - return self - - def digest(self): - """Return the **binary** (non-printable) digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Binary form. - :rtype: byte string - """ - - self._digest_done = True - - bfr = create_string_buffer(self.digest_size) - result = _raw_keccak_lib.keccak_digest(self._state.get(), - bfr, - c_size_t(self.digest_size), - c_ubyte(self._padding)) - if result: - raise ValueError("Error %d while instantiating SHA-3/224" - % result) - - self._digest_value = get_raw_buffer(bfr) - return self._digest_value - - def hexdigest(self): - """Return the **printable** digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def copy(self): - """Return a copy ("clone") of the hash object. - - The copy will have the same internal state as the original hash - object. - This can be used to efficiently compute the digests of strings that - share a common initial substring. - - :return: A hash object of the same type - """ - - clone = self.new() - result = _raw_keccak_lib.keccak_copy(self._state.get(), - clone._state.get()) - if result: - raise ValueError("Error %d while copying SHA3-224" % result) - return clone - - def new(self, data=None): - """Create a fresh SHA3-224 hash object.""" - - return type(self)(data, self._update_after_digest) - - -def new(*args, **kwargs): - """Create a new hash object. - - Args: - data (byte string/byte array/memoryview): - The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`update`. - update_after_digest (boolean): - Whether :meth:`digest` can be followed by another :meth:`update` - (default: ``False``). - - :Return: A :class:`SHA3_224_Hash` hash object - """ - - data = kwargs.pop("data", None) - update_after_digest = kwargs.pop("update_after_digest", False) - if len(args) == 1: - if data: - raise ValueError("Initial data for hash specified twice") - data = args[0] - - if kwargs: - raise TypeError("Unknown parameters: " + str(kwargs)) - - return SHA3_224_Hash(data, update_after_digest) - -# The size of the resulting hash in bytes. -digest_size = SHA3_224_Hash.digest_size - -# Input block size for HMAC -block_size = 144 diff --git a/resources/lib/deps/Cryptodome/Hash/SHA3_224.pyi b/resources/lib/deps/Cryptodome/Hash/SHA3_224.pyi deleted file mode 100644 index 2180821..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA3_224.pyi +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Union, Optional - -Buffer = Union[bytes, bytearray, memoryview] - -class SHA3_224_Hash(object): - digest_size: int - block_size: int - oid: str - def __init__(self, data: Optional[Buffer], update_after_digest: bool) -> None: ... - def update(self, data: Buffer) -> SHA3_224_Hash: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def copy(self) -> SHA3_224_Hash: ... - def new(self, data: Optional[Buffer]) -> SHA3_224_Hash: ... - -def new(__data: Buffer = ..., update_after_digest: bool = ...) -> SHA3_224_Hash: ... - -digest_size: int -block_size: int diff --git a/resources/lib/deps/Cryptodome/Hash/SHA3_256.py b/resources/lib/deps/Cryptodome/Hash/SHA3_256.py deleted file mode 100644 index 024962f..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA3_256.py +++ /dev/null @@ -1,174 +0,0 @@ -# -*- coding: utf-8 -*- -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -from Cryptodome.Util.py3compat import bord - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr, c_ubyte) - -from Cryptodome.Hash.keccak import _raw_keccak_lib - -class SHA3_256_Hash(object): - """A SHA3-256 hash object. - Do not instantiate directly. - Use the :func:`new` function. - - :ivar oid: ASN.1 Object ID - :vartype oid: string - - :ivar digest_size: the size in bytes of the resulting hash - :vartype digest_size: integer - """ - - # The size of the resulting hash in bytes. - digest_size = 32 - - # ASN.1 Object ID - oid = "2.16.840.1.101.3.4.2.8" - - # Input block size for HMAC - block_size = 136 - - def __init__(self, data, update_after_digest): - self._update_after_digest = update_after_digest - self._digest_done = False - self._padding = 0x06 - - state = VoidPointer() - result = _raw_keccak_lib.keccak_init(state.address_of(), - c_size_t(self.digest_size * 2), - c_ubyte(24)) - if result: - raise ValueError("Error %d while instantiating SHA-3/256" - % result) - self._state = SmartPointer(state.get(), - _raw_keccak_lib.keccak_destroy) - if data: - self.update(data) - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Args: - data (byte string/byte array/memoryview): The next chunk of the message being hashed. - """ - - if self._digest_done and not self._update_after_digest: - raise TypeError("You can only call 'digest' or 'hexdigest' on this object") - - result = _raw_keccak_lib.keccak_absorb(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data)) - ) - if result: - raise ValueError("Error %d while updating SHA-3/256" - % result) - return self - - def digest(self): - """Return the **binary** (non-printable) digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Binary form. - :rtype: byte string - """ - - self._digest_done = True - - bfr = create_string_buffer(self.digest_size) - result = _raw_keccak_lib.keccak_digest(self._state.get(), - bfr, - c_size_t(self.digest_size), - c_ubyte(self._padding)) - if result: - raise ValueError("Error %d while instantiating SHA-3/256" - % result) - - self._digest_value = get_raw_buffer(bfr) - return self._digest_value - - def hexdigest(self): - """Return the **printable** digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def copy(self): - """Return a copy ("clone") of the hash object. - - The copy will have the same internal state as the original hash - object. - This can be used to efficiently compute the digests of strings that - share a common initial substring. - - :return: A hash object of the same type - """ - - clone = self.new() - result = _raw_keccak_lib.keccak_copy(self._state.get(), - clone._state.get()) - if result: - raise ValueError("Error %d while copying SHA3-256" % result) - return clone - - def new(self, data=None): - """Create a fresh SHA3-256 hash object.""" - - return type(self)(data, self._update_after_digest) - - -def new(*args, **kwargs): - """Create a new hash object. - - Args: - data (byte string/byte array/memoryview): - The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`update`. - update_after_digest (boolean): - Whether :meth:`digest` can be followed by another :meth:`update` - (default: ``False``). - - :Return: A :class:`SHA3_256_Hash` hash object - """ - - data = kwargs.pop("data", None) - update_after_digest = kwargs.pop("update_after_digest", False) - if len(args) == 1: - if data: - raise ValueError("Initial data for hash specified twice") - data = args[0] - - if kwargs: - raise TypeError("Unknown parameters: " + str(kwargs)) - - return SHA3_256_Hash(data, update_after_digest) - -# The size of the resulting hash in bytes. -digest_size = SHA3_256_Hash.digest_size - -# Input block size for HMAC -block_size = 136 diff --git a/resources/lib/deps/Cryptodome/Hash/SHA3_256.pyi b/resources/lib/deps/Cryptodome/Hash/SHA3_256.pyi deleted file mode 100644 index 88436bd..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA3_256.pyi +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Union, Optional - -Buffer = Union[bytes, bytearray, memoryview] - -class SHA3_256_Hash(object): - digest_size: int - block_size: int - oid: str - def __init__(self, data: Optional[Buffer], update_after_digest: bool) -> None: ... - def update(self, data: Buffer) -> SHA3_256_Hash: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def copy(self) -> SHA3_256_Hash: ... - def new(self, data: Optional[Buffer]) -> SHA3_256_Hash: ... - -def new(__data: Buffer = ..., update_after_digest: bool = ...) -> SHA3_256_Hash: ... - -digest_size: int -block_size: int diff --git a/resources/lib/deps/Cryptodome/Hash/SHA3_384.py b/resources/lib/deps/Cryptodome/Hash/SHA3_384.py deleted file mode 100644 index 26eeb79..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA3_384.py +++ /dev/null @@ -1,179 +0,0 @@ -# -*- coding: utf-8 -*- -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -from Cryptodome.Util.py3compat import bord - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr, c_ubyte) - -from Cryptodome.Hash.keccak import _raw_keccak_lib - -class SHA3_384_Hash(object): - """A SHA3-384 hash object. - Do not instantiate directly. - Use the :func:`new` function. - - :ivar oid: ASN.1 Object ID - :vartype oid: string - - :ivar digest_size: the size in bytes of the resulting hash - :vartype digest_size: integer - """ - - # The size of the resulting hash in bytes. - digest_size = 48 - - # ASN.1 Object ID - oid = "2.16.840.1.101.3.4.2.9" - - # Input block size for HMAC - block_size = 104 - - def __init__(self, data, update_after_digest): - self._update_after_digest = update_after_digest - self._digest_done = False - self._padding = 0x06 - - state = VoidPointer() - result = _raw_keccak_lib.keccak_init(state.address_of(), - c_size_t(self.digest_size * 2), - c_ubyte(24)) - if result: - raise ValueError("Error %d while instantiating SHA-3/384" - % result) - self._state = SmartPointer(state.get(), - _raw_keccak_lib.keccak_destroy) - if data: - self.update(data) - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Args: - data (byte string/byte array/memoryview): The next chunk of the message being hashed. - """ - - if self._digest_done and not self._update_after_digest: - raise TypeError("You can only call 'digest' or 'hexdigest' on this object") - - result = _raw_keccak_lib.keccak_absorb(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data))) - if result: - raise ValueError("Error %d while updating SHA-3/384" - % result) - return self - - def digest(self): - """Return the **binary** (non-printable) digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Binary form. - :rtype: byte string - """ - - self._digest_done = True - - bfr = create_string_buffer(self.digest_size) - result = _raw_keccak_lib.keccak_digest(self._state.get(), - bfr, - c_size_t(self.digest_size), - c_ubyte(self._padding)) - if result: - raise ValueError("Error %d while instantiating SHA-3/384" - % result) - - self._digest_value = get_raw_buffer(bfr) - return self._digest_value - - def hexdigest(self): - """Return the **printable** digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def copy(self): - """Return a copy ("clone") of the hash object. - - The copy will have the same internal state as the original hash - object. - This can be used to efficiently compute the digests of strings that - share a common initial substring. - - :return: A hash object of the same type - """ - - clone = self.new() - result = _raw_keccak_lib.keccak_copy(self._state.get(), - clone._state.get()) - if result: - raise ValueError("Error %d while copying SHA3-384" % result) - return clone - - def new(self, data=None): - """Create a fresh SHA3-256 hash object.""" - - return type(self)(data, self._update_after_digest) - - - def new(self, data=None): - """Create a fresh SHA3-384 hash object.""" - - return type(self)(data, self._update_after_digest) - - -def new(*args, **kwargs): - """Create a new hash object. - - Args: - data (byte string/byte array/memoryview): - The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`update`. - update_after_digest (boolean): - Whether :meth:`digest` can be followed by another :meth:`update` - (default: ``False``). - - :Return: A :class:`SHA3_384_Hash` hash object - """ - - data = kwargs.pop("data", None) - update_after_digest = kwargs.pop("update_after_digest", False) - if len(args) == 1: - if data: - raise ValueError("Initial data for hash specified twice") - data = args[0] - - if kwargs: - raise TypeError("Unknown parameters: " + str(kwargs)) - - return SHA3_384_Hash(data, update_after_digest) - -# The size of the resulting hash in bytes. -digest_size = SHA3_384_Hash.digest_size - -# Input block size for HMAC -block_size = 104 diff --git a/resources/lib/deps/Cryptodome/Hash/SHA3_384.pyi b/resources/lib/deps/Cryptodome/Hash/SHA3_384.pyi deleted file mode 100644 index 98d00c6..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA3_384.pyi +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Union, Optional - -Buffer = Union[bytes, bytearray, memoryview] - -class SHA3_384_Hash(object): - digest_size: int - block_size: int - oid: str - def __init__(self, data: Optional[Buffer], update_after_digest: bool) -> None: ... - def update(self, data: Buffer) -> SHA3_384_Hash: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def copy(self) -> SHA3_384_Hash: ... - def new(self, data: Optional[Buffer]) -> SHA3_384_Hash: ... - -def new(__data: Buffer = ..., update_after_digest: bool = ...) -> SHA3_384_Hash: ... - -digest_size: int -block_size: int diff --git a/resources/lib/deps/Cryptodome/Hash/SHA3_512.py b/resources/lib/deps/Cryptodome/Hash/SHA3_512.py deleted file mode 100644 index 99b1c37..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA3_512.py +++ /dev/null @@ -1,174 +0,0 @@ -# -*- coding: utf-8 -*- -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -from Cryptodome.Util.py3compat import bord - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr, c_ubyte) - -from Cryptodome.Hash.keccak import _raw_keccak_lib - -class SHA3_512_Hash(object): - """A SHA3-512 hash object. - Do not instantiate directly. - Use the :func:`new` function. - - :ivar oid: ASN.1 Object ID - :vartype oid: string - - :ivar digest_size: the size in bytes of the resulting hash - :vartype digest_size: integer - """ - - # The size of the resulting hash in bytes. - digest_size = 64 - - # ASN.1 Object ID - oid = "2.16.840.1.101.3.4.2.10" - - # Input block size for HMAC - block_size = 72 - - def __init__(self, data, update_after_digest): - self._update_after_digest = update_after_digest - self._digest_done = False - self._padding = 0x06 - - state = VoidPointer() - result = _raw_keccak_lib.keccak_init(state.address_of(), - c_size_t(self.digest_size * 2), - c_ubyte(24)) - if result: - raise ValueError("Error %d while instantiating SHA-3/512" - % result) - self._state = SmartPointer(state.get(), - _raw_keccak_lib.keccak_destroy) - if data: - self.update(data) - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Args: - data (byte string/byte array/memoryview): The next chunk of the message being hashed. - """ - - if self._digest_done and not self._update_after_digest: - raise TypeError("You can only call 'digest' or 'hexdigest' on this object") - - result = _raw_keccak_lib.keccak_absorb(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data))) - if result: - raise ValueError("Error %d while updating SHA-3/512" - % result) - return self - - def digest(self): - - """Return the **binary** (non-printable) digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Binary form. - :rtype: byte string - """ - - self._digest_done = True - - bfr = create_string_buffer(self.digest_size) - result = _raw_keccak_lib.keccak_digest(self._state.get(), - bfr, - c_size_t(self.digest_size), - c_ubyte(self._padding)) - if result: - raise ValueError("Error %d while instantiating SHA-3/512" - % result) - - self._digest_value = get_raw_buffer(bfr) - return self._digest_value - - def hexdigest(self): - """Return the **printable** digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def copy(self): - """Return a copy ("clone") of the hash object. - - The copy will have the same internal state as the original hash - object. - This can be used to efficiently compute the digests of strings that - share a common initial substring. - - :return: A hash object of the same type - """ - - clone = self.new() - result = _raw_keccak_lib.keccak_copy(self._state.get(), - clone._state.get()) - if result: - raise ValueError("Error %d while copying SHA3-512" % result) - return clone - - def new(self, data=None): - """Create a fresh SHA3-521 hash object.""" - - return type(self)(data, self._update_after_digest) - - -def new(*args, **kwargs): - """Create a new hash object. - - Args: - data (byte string/byte array/memoryview): - The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`update`. - update_after_digest (boolean): - Whether :meth:`digest` can be followed by another :meth:`update` - (default: ``False``). - - :Return: A :class:`SHA3_512_Hash` hash object - """ - - data = kwargs.pop("data", None) - update_after_digest = kwargs.pop("update_after_digest", False) - if len(args) == 1: - if data: - raise ValueError("Initial data for hash specified twice") - data = args[0] - - if kwargs: - raise TypeError("Unknown parameters: " + str(kwargs)) - - return SHA3_512_Hash(data, update_after_digest) - -# The size of the resulting hash in bytes. -digest_size = SHA3_512_Hash.digest_size - -# Input block size for HMAC -block_size = 72 diff --git a/resources/lib/deps/Cryptodome/Hash/SHA3_512.pyi b/resources/lib/deps/Cryptodome/Hash/SHA3_512.pyi deleted file mode 100644 index cdeec16..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA3_512.pyi +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Union, Optional - -Buffer = Union[bytes, bytearray, memoryview] - -class SHA3_512_Hash(object): - digest_size: int - block_size: int - oid: str - def __init__(self, data: Optional[Buffer], update_after_digest: bool) -> None: ... - def update(self, data: Buffer) -> SHA3_512_Hash: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def copy(self) -> SHA3_512_Hash: ... - def new(self, data: Optional[Buffer]) -> SHA3_512_Hash: ... - -def new(__data: Buffer = ..., update_after_digest: bool = ...) -> SHA3_512_Hash: ... - -digest_size: int -block_size: int diff --git a/resources/lib/deps/Cryptodome/Hash/SHA512.py b/resources/lib/deps/Cryptodome/Hash/SHA512.py deleted file mode 100644 index 5066197..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA512.py +++ /dev/null @@ -1,204 +0,0 @@ -# -*- coding: utf-8 -*- -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -from Cryptodome.Util.py3compat import bord - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr) - -_raw_sha512_lib = load_pycryptodome_raw_lib("Cryptodome.Hash._SHA512", - """ - int SHA512_init(void **shaState, - size_t digest_size); - int SHA512_destroy(void *shaState); - int SHA512_update(void *hs, - const uint8_t *buf, - size_t len); - int SHA512_digest(const void *shaState, - uint8_t *digest, - size_t digest_size); - int SHA512_copy(const void *src, void *dst); - - int SHA512_pbkdf2_hmac_assist(const void *inner, - const void *outer, - const uint8_t *first_digest, - uint8_t *final_digest, - size_t iterations, - size_t digest_size); - """) - -class SHA512Hash(object): - """A SHA-512 hash object (possibly in its truncated version SHA-512/224 or - SHA-512/256. - Do not instantiate directly. Use the :func:`new` function. - - :ivar oid: ASN.1 Object ID - :vartype oid: string - - :ivar block_size: the size in bytes of the internal message block, - input to the compression function - :vartype block_size: integer - - :ivar digest_size: the size in bytes of the resulting hash - :vartype digest_size: integer - """ - - # The internal block size of the hash algorithm in bytes. - block_size = 128 - - def __init__(self, data, truncate): - self._truncate = truncate - - if truncate is None: - self.oid = "2.16.840.1.101.3.4.2.3" - self.digest_size = 64 - elif truncate == "224": - self.oid = "2.16.840.1.101.3.4.2.5" - self.digest_size = 28 - elif truncate == "256": - self.oid = "2.16.840.1.101.3.4.2.6" - self.digest_size = 32 - else: - raise ValueError("Incorrect truncation length. It must be '224' or '256'.") - - state = VoidPointer() - result = _raw_sha512_lib.SHA512_init(state.address_of(), - c_size_t(self.digest_size)) - if result: - raise ValueError("Error %d while instantiating SHA-512" - % result) - self._state = SmartPointer(state.get(), - _raw_sha512_lib.SHA512_destroy) - if data: - self.update(data) - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Args: - data (byte string/byte array/memoryview): The next chunk of the message being hashed. - """ - - result = _raw_sha512_lib.SHA512_update(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data))) - if result: - raise ValueError("Error %d while hashing data with SHA512" - % result) - - def digest(self): - """Return the **binary** (non-printable) digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Binary form. - :rtype: byte string - """ - - bfr = create_string_buffer(self.digest_size) - result = _raw_sha512_lib.SHA512_digest(self._state.get(), - bfr, - c_size_t(self.digest_size)) - if result: - raise ValueError("Error %d while making SHA512 digest" - % result) - - return get_raw_buffer(bfr) - - def hexdigest(self): - """Return the **printable** digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def copy(self): - """Return a copy ("clone") of the hash object. - - The copy will have the same internal state as the original hash - object. - This can be used to efficiently compute the digests of strings that - share a common initial substring. - - :return: A hash object of the same type - """ - - clone = SHA512Hash(None, self._truncate) - result = _raw_sha512_lib.SHA512_copy(self._state.get(), - clone._state.get()) - if result: - raise ValueError("Error %d while copying SHA512" % result) - return clone - - def new(self, data=None): - """Create a fresh SHA-512 hash object.""" - - return SHA512Hash(data, self._truncate) - - -def new(data=None, truncate=None): - """Create a new hash object. - - Args: - data (bytes/bytearray/memoryview): - Optional. The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`SHA512Hash.update`. - truncate (string): - Optional. The desired length of the digest. It can be either "224" or - "256". If not present, the digest is 512 bits long. - Passing this parameter is **not** equivalent to simply truncating - the output digest. - - :Return: A :class:`SHA512Hash` hash object - """ - - return SHA512Hash(data, truncate) - - -# The size of the full SHA-512 hash in bytes. -digest_size = 64 - -# The internal block size of the hash algorithm in bytes. -block_size = 128 - - -def _pbkdf2_hmac_assist(inner, outer, first_digest, iterations): - """Compute the expensive inner loop in PBKDF-HMAC.""" - - assert iterations > 0 - - bfr = create_string_buffer(len(first_digest)); - result = _raw_sha512_lib.SHA512_pbkdf2_hmac_assist( - inner._state.get(), - outer._state.get(), - first_digest, - bfr, - c_size_t(iterations), - c_size_t(len(first_digest))) - - if result: - raise ValueError("Error %d with PBKDF2-HMAC assist for SHA512" % result) - - return get_raw_buffer(bfr) diff --git a/resources/lib/deps/Cryptodome/Hash/SHA512.pyi b/resources/lib/deps/Cryptodome/Hash/SHA512.pyi deleted file mode 100644 index f219ee9..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHA512.pyi +++ /dev/null @@ -1,22 +0,0 @@ -from typing import Union, Optional - -Buffer = Union[bytes, bytearray, memoryview] - -class SHA512Hash(object): - digest_size: int - block_size: int - oid: str - - def __init__(self, - data: Optional[Buffer], - truncate: Optional[str]) -> None: ... - def update(self, data: Buffer) -> None: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def copy(self) -> SHA512Hash: ... - def new(self, data: Optional[Buffer] = ...) -> SHA512Hash: ... - -def new(data: Optional[Buffer] = ..., - truncate: Optional[str] = ...) -> SHA512Hash: ... -digest_size: int -block_size: int diff --git a/resources/lib/deps/Cryptodome/Hash/SHAKE128.py b/resources/lib/deps/Cryptodome/Hash/SHAKE128.py deleted file mode 100644 index 5bde2b6..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHAKE128.py +++ /dev/null @@ -1,129 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2015, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from Cryptodome.Util.py3compat import bord - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr, c_ubyte) - -from Cryptodome.Hash.keccak import _raw_keccak_lib - -class SHAKE128_XOF(object): - """A SHAKE128 hash object. - Do not instantiate directly. - Use the :func:`new` function. - - :ivar oid: ASN.1 Object ID - :vartype oid: string - """ - - # ASN.1 Object ID - oid = "2.16.840.1.101.3.4.2.11" - - def __init__(self, data=None): - state = VoidPointer() - result = _raw_keccak_lib.keccak_init(state.address_of(), - c_size_t(32), - c_ubyte(24)) - if result: - raise ValueError("Error %d while instantiating SHAKE128" - % result) - self._state = SmartPointer(state.get(), - _raw_keccak_lib.keccak_destroy) - self._is_squeezing = False - self._padding = 0x1F - if data: - self.update(data) - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Args: - data (byte string/byte array/memoryview): The next chunk of the message being hashed. - """ - - if self._is_squeezing: - raise TypeError("You cannot call 'update' after the first 'read'") - - result = _raw_keccak_lib.keccak_absorb(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data))) - if result: - raise ValueError("Error %d while updating SHAKE128 state" - % result) - return self - - def read(self, length): - """ - Compute the next piece of XOF output. - - .. note:: - You cannot use :meth:`update` anymore after the first call to - :meth:`read`. - - Args: - length (integer): the amount of bytes this method must return - - :return: the next piece of XOF output (of the given length) - :rtype: byte string - """ - - self._is_squeezing = True - bfr = create_string_buffer(length) - result = _raw_keccak_lib.keccak_squeeze(self._state.get(), - bfr, - c_size_t(length), - c_ubyte(self._padding)) - if result: - raise ValueError("Error %d while extracting from SHAKE128" - % result) - - return get_raw_buffer(bfr) - - def new(self, data=None): - return type(self)(data=data) - - -def new(data=None): - """Return a fresh instance of a SHAKE128 object. - - Args: - data (bytes/bytearray/memoryview): - The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`update`. - Optional. - - :Return: A :class:`SHAKE128_XOF` object - """ - - return SHAKE128_XOF(data=data) diff --git a/resources/lib/deps/Cryptodome/Hash/SHAKE128.pyi b/resources/lib/deps/Cryptodome/Hash/SHAKE128.pyi deleted file mode 100644 index f618881..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHAKE128.pyi +++ /dev/null @@ -1,13 +0,0 @@ -from typing import Union, Optional - -Buffer = Union[bytes, bytearray, memoryview] - -class SHAKE128_XOF(object): - oid: str - def __init__(self, - data: Optional[Buffer] = ...) -> None: ... - def update(self, data: Buffer) -> SHAKE128_XOF: ... - def read(self, length: int) -> bytes: ... - def new(self, data: Optional[Buffer] = ...) -> SHAKE128_XOF: ... - -def new(data: Optional[Buffer] = ...) -> SHAKE128_XOF: ... diff --git a/resources/lib/deps/Cryptodome/Hash/SHAKE256.py b/resources/lib/deps/Cryptodome/Hash/SHAKE256.py deleted file mode 100644 index 8c37f6a..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHAKE256.py +++ /dev/null @@ -1,130 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2015, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from Cryptodome.Util.py3compat import bord - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr, c_ubyte) - -from Cryptodome.Hash.keccak import _raw_keccak_lib - -class SHAKE256_XOF(object): - """A SHAKE256 hash object. - Do not instantiate directly. - Use the :func:`new` function. - - :ivar oid: ASN.1 Object ID - :vartype oid: string - """ - - # ASN.1 Object ID - oid = "2.16.840.1.101.3.4.2.12" - - def __init__(self, data=None): - state = VoidPointer() - result = _raw_keccak_lib.keccak_init(state.address_of(), - c_size_t(64), - c_ubyte(24)) - if result: - raise ValueError("Error %d while instantiating SHAKE256" - % result) - self._state = SmartPointer(state.get(), - _raw_keccak_lib.keccak_destroy) - self._is_squeezing = False - self._padding = 0x1F - - if data: - self.update(data) - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Args: - data (byte string/byte array/memoryview): The next chunk of the message being hashed. - """ - - if self._is_squeezing: - raise TypeError("You cannot call 'update' after the first 'read'") - - result = _raw_keccak_lib.keccak_absorb(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data))) - if result: - raise ValueError("Error %d while updating SHAKE256 state" - % result) - return self - - def read(self, length): - """ - Compute the next piece of XOF output. - - .. note:: - You cannot use :meth:`update` anymore after the first call to - :meth:`read`. - - Args: - length (integer): the amount of bytes this method must return - - :return: the next piece of XOF output (of the given length) - :rtype: byte string - """ - - self._is_squeezing = True - bfr = create_string_buffer(length) - result = _raw_keccak_lib.keccak_squeeze(self._state.get(), - bfr, - c_size_t(length), - c_ubyte(self._padding)) - if result: - raise ValueError("Error %d while extracting from SHAKE256" - % result) - - return get_raw_buffer(bfr) - - def new(self, data=None): - return type(self)(data=data) - - -def new(data=None): - """Return a fresh instance of a SHAKE256 object. - - Args: - data (bytes/bytearray/memoryview): - The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`update`. - Optional. - - :Return: A :class:`SHAKE256_XOF` object - """ - - return SHAKE256_XOF(data=data) diff --git a/resources/lib/deps/Cryptodome/Hash/SHAKE256.pyi b/resources/lib/deps/Cryptodome/Hash/SHAKE256.pyi deleted file mode 100644 index 029347a..0000000 --- a/resources/lib/deps/Cryptodome/Hash/SHAKE256.pyi +++ /dev/null @@ -1,13 +0,0 @@ -from typing import Union, Optional - -Buffer = Union[bytes, bytearray, memoryview] - -class SHAKE256_XOF(object): - oid: str - def __init__(self, - data: Optional[Buffer] = ...) -> None: ... - def update(self, data: Buffer) -> SHAKE256_XOF: ... - def read(self, length: int) -> bytes: ... - def new(self, data: Optional[Buffer] = ...) -> SHAKE256_XOF: ... - -def new(data: Optional[Buffer] = ...) -> SHAKE256_XOF: ... diff --git a/resources/lib/deps/Cryptodome/Hash/TupleHash128.py b/resources/lib/deps/Cryptodome/Hash/TupleHash128.py deleted file mode 100644 index 49aeccc..0000000 --- a/resources/lib/deps/Cryptodome/Hash/TupleHash128.py +++ /dev/null @@ -1,136 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2021, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from Cryptodome.Util.py3compat import bord, is_bytes, tobytes - -from . import cSHAKE128 -from .cSHAKE128 import _encode_str, _right_encode - - -class TupleHash(object): - """A Tuple hash object. - Do not instantiate directly. - Use the :func:`new` function. - """ - - def __init__(self, custom, cshake, digest_size): - - self.digest_size = digest_size - - self._cshake = cshake._new(b'', custom, b'TupleHash') - self._digest = None - - def update(self, *data): - """Authenticate the next tuple of byte strings. - TupleHash guarantees the logical separation between each byte string. - - Args: - data (bytes/bytearray/memoryview): One or more items to hash. - """ - - if self._digest is not None: - raise TypeError("You cannot call 'update' after 'digest' or 'hexdigest'") - - for item in data: - if not is_bytes(item): - raise TypeError("You can only call 'update' on bytes" ) - self._cshake.update(_encode_str(item)) - - return self - - def digest(self): - """Return the **binary** (non-printable) digest of the tuple of byte strings. - - :return: The hash digest. Binary form. - :rtype: byte string - """ - - if self._digest is None: - self._cshake.update(_right_encode(self.digest_size * 8)) - self._digest = self._cshake.read(self.digest_size) - - return self._digest - - def hexdigest(self): - """Return the **printable** digest of the tuple of byte strings. - - :return: The hash digest. Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) for x in tuple(self.digest())]) - - def new(self, **kwargs): - """Return a new instance of a TupleHash object. - See :func:`new`. - """ - - if "digest_bytes" not in kwargs and "digest_bits" not in kwargs: - kwargs["digest_bytes"] = self.digest_size - - return new(**kwargs) - - -def new(**kwargs): - """Create a new TupleHash128 object. - - Args: - digest_bytes (integer): - Optional. The size of the digest, in bytes. - Default is 64. Minimum is 8. - digest_bits (integer): - Optional and alternative to ``digest_bytes``. - The size of the digest, in bits (and in steps of 8). - Default is 512. Minimum is 64. - custom (bytes): - Optional. - A customization bytestring (``S`` in SP 800-185). - - :Return: A :class:`TupleHash` object - """ - - digest_bytes = kwargs.pop("digest_bytes", None) - digest_bits = kwargs.pop("digest_bits", None) - if None not in (digest_bytes, digest_bits): - raise TypeError("Only one digest parameter must be provided") - if (None, None) == (digest_bytes, digest_bits): - digest_bytes = 64 - if digest_bytes is not None: - if digest_bytes < 8: - raise ValueError("'digest_bytes' must be at least 8") - else: - if digest_bits < 64 or digest_bits % 8: - raise ValueError("'digest_bytes' must be at least 64 " - "in steps of 8") - digest_bytes = digest_bits // 8 - - custom = kwargs.pop("custom", b'') - - return TupleHash(custom, cSHAKE128, digest_bytes) diff --git a/resources/lib/deps/Cryptodome/Hash/TupleHash128.pyi b/resources/lib/deps/Cryptodome/Hash/TupleHash128.pyi deleted file mode 100644 index 2e0ea83..0000000 --- a/resources/lib/deps/Cryptodome/Hash/TupleHash128.pyi +++ /dev/null @@ -1,22 +0,0 @@ -from typing import Any, Union, List, Tuple -from types import ModuleType - -Buffer = Union[bytes, bytearray, memoryview] - -class TupleHash(object): - digest_size: int - def __init__(self, - custom: bytes, - cshake: ModuleType, - digest_size: int) -> None: ... - def update(self, *data: Buffer) -> TupleHash: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def new(self, - digest_bytes: int = ..., - digest_bits: int = ..., - custom: int = ...) -> TupleHash: ... - -def new(digest_bytes: int = ..., - digest_bits: int = ..., - custom: int = ...) -> TupleHash: ... diff --git a/resources/lib/deps/Cryptodome/Hash/TupleHash256.py b/resources/lib/deps/Cryptodome/Hash/TupleHash256.py deleted file mode 100644 index 40a824a..0000000 --- a/resources/lib/deps/Cryptodome/Hash/TupleHash256.py +++ /dev/null @@ -1,70 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2021, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from . import cSHAKE256 -from .TupleHash128 import TupleHash - - -def new(**kwargs): - """Create a new TupleHash256 object. - - Args: - digest_bytes (integer): - Optional. The size of the digest, in bytes. - Default is 64. Minimum is 8. - digest_bits (integer): - Optional and alternative to ``digest_bytes``. - The size of the digest, in bits (and in steps of 8). - Default is 512. Minimum is 64. - custom (bytes): - Optional. - A customization bytestring (``S`` in SP 800-185). - - :Return: A :class:`TupleHash` object - """ - - digest_bytes = kwargs.pop("digest_bytes", None) - digest_bits = kwargs.pop("digest_bits", None) - if None not in (digest_bytes, digest_bits): - raise TypeError("Only one digest parameter must be provided") - if (None, None) == (digest_bytes, digest_bits): - digest_bytes = 64 - if digest_bytes is not None: - if digest_bytes < 8: - raise ValueError("'digest_bytes' must be at least 8") - else: - if digest_bits < 64 or digest_bits % 8: - raise ValueError("'digest_bytes' must be at least 64 " - "in steps of 8") - digest_bytes = digest_bits // 8 - - custom = kwargs.pop("custom", b'') - - return TupleHash(custom, cSHAKE256, digest_bytes) diff --git a/resources/lib/deps/Cryptodome/Hash/TupleHash256.pyi b/resources/lib/deps/Cryptodome/Hash/TupleHash256.pyi deleted file mode 100644 index 82d943f..0000000 --- a/resources/lib/deps/Cryptodome/Hash/TupleHash256.pyi +++ /dev/null @@ -1,5 +0,0 @@ -from .TupleHash128 import TupleHash - -def new(digest_bytes: int = ..., - digest_bits: int = ..., - custom: int = ...) -> TupleHash: ... diff --git a/resources/lib/deps/Cryptodome/Hash/TurboSHAKE128.py b/resources/lib/deps/Cryptodome/Hash/TurboSHAKE128.py deleted file mode 100644 index 92ac59e..0000000 --- a/resources/lib/deps/Cryptodome/Hash/TurboSHAKE128.py +++ /dev/null @@ -1,112 +0,0 @@ -from Cryptodome.Util._raw_api import (VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr, c_ubyte) - -from Cryptodome.Util.number import long_to_bytes -from Cryptodome.Util.py3compat import bchr - -from .keccak import _raw_keccak_lib - - -class TurboSHAKE(object): - """A TurboSHAKE hash object. - Do not instantiate directly. - Use the :func:`new` function. - """ - - def __init__(self, capacity, domain_separation, data): - - state = VoidPointer() - result = _raw_keccak_lib.keccak_init(state.address_of(), - c_size_t(capacity), - c_ubyte(12)) # Reduced number of rounds - if result: - raise ValueError("Error %d while instantiating TurboSHAKE" - % result) - self._state = SmartPointer(state.get(), _raw_keccak_lib.keccak_destroy) - - self._is_squeezing = False - self._capacity = capacity - self._domain = domain_separation - - if data: - self.update(data) - - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Args: - data (byte string/byte array/memoryview): The next chunk of the message being hashed. - """ - - if self._is_squeezing: - raise TypeError("You cannot call 'update' after the first 'read'") - - result = _raw_keccak_lib.keccak_absorb(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data))) - if result: - raise ValueError("Error %d while updating TurboSHAKE state" - % result) - return self - - def read(self, length): - """ - Compute the next piece of XOF output. - - .. note:: - You cannot use :meth:`update` anymore after the first call to - :meth:`read`. - - Args: - length (integer): the amount of bytes this method must return - - :return: the next piece of XOF output (of the given length) - :rtype: byte string - """ - - self._is_squeezing = True - bfr = create_string_buffer(length) - result = _raw_keccak_lib.keccak_squeeze(self._state.get(), - bfr, - c_size_t(length), - c_ubyte(self._domain)) - if result: - raise ValueError("Error %d while extracting from TurboSHAKE" - % result) - - return get_raw_buffer(bfr) - - def new(self, data=None): - return type(self)(self._capacity, self._domain, data) - - def _reset(self): - result = _raw_keccak_lib.keccak_reset(self._state.get()) - if result: - raise ValueError("Error %d while resetting TurboSHAKE state" - % result) - self._is_squeezing = False - - -def new(**kwargs): - """Create a new TurboSHAKE128 object. - - Args: - domain (integer): - Optional - A domain separation byte, between 0x01 and 0x7F. - The default value is 0x1F. - data (bytes/bytearray/memoryview): - Optional - The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`update`. - - :Return: A :class:`TurboSHAKE` object - """ - - domain_separation = kwargs.get('domain', 0x1F) - if not (0x01 <= domain_separation <= 0x7F): - raise ValueError("Incorrect domain separation value (%d)" % - domain_separation) - data = kwargs.get('data') - return TurboSHAKE(32, domain_separation, data=data) diff --git a/resources/lib/deps/Cryptodome/Hash/TurboSHAKE128.pyi b/resources/lib/deps/Cryptodome/Hash/TurboSHAKE128.pyi deleted file mode 100644 index d74c9c0..0000000 --- a/resources/lib/deps/Cryptodome/Hash/TurboSHAKE128.pyi +++ /dev/null @@ -1,17 +0,0 @@ -from typing import Union, Optional -from typing_extensions import TypedDict, Unpack, NotRequired - -Buffer = Union[bytes, bytearray, memoryview] - -class TurboSHAKE(object): - - def __init__(self, capacity: int, domain_separation: int, data: Union[Buffer, None]) -> None: ... - def update(self, data: Buffer) -> TurboSHAKE : ... - def read(self, length: int) -> bytes: ... - def new(self, data: Optional[Buffer]=None) -> TurboSHAKE: ... - -class Args(TypedDict): - domain: NotRequired[int] - data: NotRequired[Buffer] - -def new(**kwargs: Unpack[Args]) -> TurboSHAKE: ... diff --git a/resources/lib/deps/Cryptodome/Hash/TurboSHAKE256.py b/resources/lib/deps/Cryptodome/Hash/TurboSHAKE256.py deleted file mode 100644 index ce27a48..0000000 --- a/resources/lib/deps/Cryptodome/Hash/TurboSHAKE256.py +++ /dev/null @@ -1,22 +0,0 @@ -from .TurboSHAKE128 import TurboSHAKE - -def new(**kwargs): - """Create a new TurboSHAKE256 object. - - Args: - domain (integer): - Optional - A domain separation byte, between 0x01 and 0x7F. - The default value is 0x1F. - data (bytes/bytearray/memoryview): - Optional - The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`update`. - - :Return: A :class:`TurboSHAKE` object - """ - - domain_separation = kwargs.get('domain', 0x1F) - if not (0x01 <= domain_separation <= 0x7F): - raise ValueError("Incorrect domain separation value (%d)" % - domain_separation) - data = kwargs.get('data') - return TurboSHAKE(64, domain_separation, data=data) diff --git a/resources/lib/deps/Cryptodome/Hash/TurboSHAKE256.pyi b/resources/lib/deps/Cryptodome/Hash/TurboSHAKE256.pyi deleted file mode 100644 index 561e946..0000000 --- a/resources/lib/deps/Cryptodome/Hash/TurboSHAKE256.pyi +++ /dev/null @@ -1,12 +0,0 @@ -from typing import Union -from typing_extensions import TypedDict, Unpack, NotRequired - -from .TurboSHAKE128 import TurboSHAKE - -Buffer = Union[bytes, bytearray, memoryview] - -class Args(TypedDict): - domain: NotRequired[int] - data: NotRequired[Buffer] - -def new(**kwargs: Unpack[Args]) -> TurboSHAKE: ... diff --git a/resources/lib/deps/Cryptodome/Hash/_BLAKE2b.abi3.so b/resources/lib/deps/Cryptodome/Hash/_BLAKE2b.abi3.so deleted file mode 100755 index 40cf664bb6b7068a84f1a19a28269813270449dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27424 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 diff --git a/resources/lib/deps/Cryptodome/Hash/_BLAKE2s.abi3.so b/resources/lib/deps/Cryptodome/Hash/_BLAKE2s.abi3.so deleted file mode 100755 index 04a1ace0a89d2c21f112ede4c0ae7d97f319e7d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26952 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 diff --git a/resources/lib/deps/Cryptodome/Hash/_MD5.abi3.so b/resources/lib/deps/Cryptodome/Hash/_MD5.abi3.so deleted file mode 100755 index b22cf365e3d0a6bad52864a2c54bc8e2c60ce348..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32008 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 GY5iZnBQc%; diff --git a/resources/lib/deps/Cryptodome/Hash/_SHA1.abi3.so b/resources/lib/deps/Cryptodome/Hash/_SHA1.abi3.so deleted file mode 100755 index acd08adca11a51c5032d564d013d6e2632d65a49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 72064 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 z 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 diff --git a/resources/lib/deps/Cryptodome/Hash/_SHA224.abi3.so b/resources/lib/deps/Cryptodome/Hash/_SHA224.abi3.so deleted file mode 100755 index 9cf3ef6de1034b0309a98b92536d46dee7bde02b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45768 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 Wx1Ua9BgtR+U%LFvFoCeF@_zyFBYFe? diff --git a/resources/lib/deps/Cryptodome/Hash/_SHA384.abi3.so b/resources/lib/deps/Cryptodome/Hash/_SHA384.abi3.so deleted file mode 100755 index 7c72fd09c9f497c295df96b5948e242ace07e567..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54160 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

K9`!?` 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 IAuOu=A7NDCvj6}9 diff --git a/resources/lib/deps/Cryptodome/Hash/_SHA512.abi3.so b/resources/lib/deps/Cryptodome/Hash/_SHA512.abi3.so deleted file mode 100755 index 058653c46c3e0b15ddfa221cebf1dadec06347c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54200 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 Hh${aN`FI_Q diff --git a/resources/lib/deps/Cryptodome/Hash/__init__.py b/resources/lib/deps/Cryptodome/Hash/__init__.py deleted file mode 100644 index 80446e4..0000000 --- a/resources/lib/deps/Cryptodome/Hash/__init__.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- coding: utf-8 -*- -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -__all__ = ['HMAC', 'MD2', 'MD4', 'MD5', 'RIPEMD160', 'SHA1', - 'SHA224', 'SHA256', 'SHA384', 'SHA512', - 'SHA3_224', 'SHA3_256', 'SHA3_384', 'SHA3_512', - 'CMAC', 'Poly1305', - 'cSHAKE128', 'cSHAKE256', 'KMAC128', 'KMAC256', - 'TupleHash128', 'TupleHash256', 'KangarooTwelve', - 'TurboSHAKE128', 'TurboSHAKE256'] - -def new(name): - """Return a new hash instance, based on its name or - on its ASN.1 Object ID""" - - name = name.upper() - if name in ("1.3.14.3.2.26", "SHA1", "SHA-1"): - from . import SHA1 - return SHA1.new() - if name in ("2.16.840.1.101.3.4.2.4", "SHA224", "SHA-224"): - from . import SHA224 - return SHA224.new() - if name in ("2.16.840.1.101.3.4.2.1", "SHA256", "SHA-256"): - from . import SHA256 - return SHA256.new() - if name in ("2.16.840.1.101.3.4.2.2", "SHA384", "SHA-384"): - from . import SHA384 - return SHA384.new() - if name in ("2.16.840.1.101.3.4.2.3", "SHA512", "SHA-512"): - from . import SHA512 - return SHA512.new() - if name in ("2.16.840.1.101.3.4.2.5", "SHA512-224", "SHA-512-224"): - from . import SHA512 - return SHA512.new(truncate='224') - if name in ("2.16.840.1.101.3.4.2.6", "SHA512-256", "SHA-512-256"): - from . import SHA512 - return SHA512.new(truncate='256') - if name in ("2.16.840.1.101.3.4.2.7", "SHA3-224", "SHA-3-224"): - from . import SHA3_224 - return SHA3_224.new() - if name in ("2.16.840.1.101.3.4.2.8", "SHA3-256", "SHA-3-256"): - from . import SHA3_256 - return SHA3_256.new() - if name in ("2.16.840.1.101.3.4.2.9", "SHA3-384", "SHA-3-384"): - from . import SHA3_384 - return SHA3_384.new() - if name in ("2.16.840.1.101.3.4.2.10", "SHA3-512", "SHA-3-512"): - from . import SHA3_512 - return SHA3_512.new() - else: - raise ValueError("Unknown hash %s" % str(name)) - diff --git a/resources/lib/deps/Cryptodome/Hash/__init__.pyi b/resources/lib/deps/Cryptodome/Hash/__init__.pyi deleted file mode 100644 index b072157..0000000 --- a/resources/lib/deps/Cryptodome/Hash/__init__.pyi +++ /dev/null @@ -1,57 +0,0 @@ -from typing import overload -from typing_extensions import Literal - -from Cryptodome.Hash.SHA1 import SHA1Hash -from Cryptodome.Hash.SHA224 import SHA224Hash -from Cryptodome.Hash.SHA256 import SHA256Hash -from Cryptodome.Hash.SHA384 import SHA384Hash -from Cryptodome.Hash.SHA512 import SHA512Hash -from Cryptodome.Hash.SHA3_224 import SHA3_224_Hash -from Cryptodome.Hash.SHA3_256 import SHA3_256_Hash -from Cryptodome.Hash.SHA3_384 import SHA3_384_Hash -from Cryptodome.Hash.SHA3_512 import SHA3_512_Hash - -@overload -def new(name: Literal["1.3.14.3.2.26"]) -> SHA1Hash: ... -@overload -def new(name: Literal["SHA1"]) -> SHA1Hash: ... -@overload -def new(name: Literal["2.16.840.1.101.3.4.2.4"]) -> SHA224Hash: ... -@overload -def new(name: Literal["SHA224"]) -> SHA224Hash: ... -@overload -def new(name: Literal["2.16.840.1.101.3.4.2.1"]) -> SHA256Hash: ... -@overload -def new(name: Literal["SHA256"]) -> SHA256Hash: ... -@overload -def new(name: Literal["2.16.840.1.101.3.4.2.2"]) -> SHA384Hash: ... -@overload -def new(name: Literal["SHA384"]) -> SHA384Hash: ... -@overload -def new(name: Literal["2.16.840.1.101.3.4.2.3"]) -> SHA512Hash: ... -@overload -def new(name: Literal["SHA512"]) -> SHA512Hash: ... -@overload -def new(name: Literal["2.16.840.1.101.3.4.2.5"]) -> SHA512Hash: ... -@overload -def new(name: Literal["SHA512-224"]) -> SHA512Hash: ... -@overload -def new(name: Literal["2.16.840.1.101.3.4.2.6"]) -> SHA512Hash: ... -@overload -def new(name: Literal["SHA512-256"]) -> SHA512Hash: ... -@overload -def new(name: Literal["2.16.840.1.101.3.4.2.7"]) -> SHA3_224_Hash: ... -@overload -def new(name: Literal["SHA3-224"]) -> SHA3_224_Hash: ... -@overload -def new(name: Literal["2.16.840.1.101.3.4.2.8"]) -> SHA3_256_Hash: ... -@overload -def new(name: Literal["SHA3-256"]) -> SHA3_256_Hash: ... -@overload -def new(name: Literal["2.16.840.1.101.3.4.2.9"]) -> SHA3_384_Hash: ... -@overload -def new(name: Literal["SHA3-384"]) -> SHA3_384_Hash: ... -@overload -def new(name: Literal["2.16.840.1.101.3.4.2.10"]) -> SHA3_512_Hash: ... -@overload -def new(name: Literal["SHA3-512"]) -> SHA3_512_Hash: ... diff --git a/resources/lib/deps/Cryptodome/Hash/_ghash_clmul.abi3.so b/resources/lib/deps/Cryptodome/Hash/_ghash_clmul.abi3.so deleted file mode 100755 index d13832ca9c5e0446207fc9b2a3897f4fc21d478b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58376 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 diff --git a/resources/lib/deps/Cryptodome/Hash/_ghash_portable.abi3.so b/resources/lib/deps/Cryptodome/Hash/_ghash_portable.abi3.so deleted file mode 100755 index 555c6fccf45daef5d5d8f33f1cd471a38b8f71d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25024 zcmeHP3v^WFo&WCKxsyA2%;YHvpc!Oo*L9nf% z23$r=i)*#?=rNpbx7uxOS=&WwPq!IqExLOY?6&o>b^)=1Pt?`L$C~~9@BRLh$z-se zv*+xd(~~df-v9gm{lEYB$bEcs2kIM^+Z07`QN>b0R8OYGsgZ)68zllzBPxXx=NZCd z>e|Od6R@M2#bPL=U#i0tNt*c#ohgq74bzj!3f0EVksCOUJ`wvOb2oB{pNEI4h;UMCg@z49zxyx8~JwtBHH0LIS)( zvKg}7HL#;Nzi8pKua)&(C@;E_B-6q3B;nryUNU`(Cz*cb1^8>hzZ6Ge=>a2Kq=^%GmP8}RC@naMUz%l-;)k-M zDDT;rJv_cFsSp>L{B$9vFvo&JCO=J_BLx)hHxUT5ZSC#~^o3h`!-0SZbaZuuMPLhn z2>6>C1FfOnP+Lb|IMmzJxU93gE7a7oxie(Rl1l=??JeY>rL*JKkZ5af>1z-4boYi$ zJBti$?`i339peUDIy<|AQnxkK7w+xeF(%#88w!cej?F#c_TErSYjIz9aVgP3!WN1Z z4gT6?f%4+=;sw+)8feyFvthKv|NT6!-ThNLkUX0>LMA9Yk9ppCWWKSCnT7c$Wa9J$ zKGP?zvh-<;${}wu&cyL~u94te9A7SRrvCXje*ApQuQA23xnNNkgK>PCD_n|dER5z! ze7&V{{CNMC_T+go1Ctq;%)n#@CNnUZfyoR^W?(V{pPvC^@J-#=tG!$zgt2=htesf` zePi&~y1c+1U2qDb(OIW)b{EzFBf6cOo;@*&GV4X6>8|JOF_V6dXu5h@3N`Z(n6Z z&#Q*=q;dSSa4sCok`DCI6I2RBhCjn<}KuBlW zae7jl3!!oY*URwiS0LO{>@M6zQHg!NYkeDh>wQh@*BOzodXE{Auy@3WGA{VOu{W!6s0zTR z8<8nSWQA_*e#yP-5@@&&{kuPMykRKpJ+r6Dd(a)reV7dWk&lhYAAeitJxUGo zN6%y$(RcFq)Oq(C(KD6Et%rObFrsIR;Ma)0Ug?j%0iCxzWh4IRn?-edzv?||?7iMQ zQdd4-^$o31>dJEo+Um;f1l4-?q33GKs|dH(!SUf5OHo5ib>2NS?n9&H=_GXk{gAAP zzu$f6Dbn6kKeQsH7A^txS_lE1@VLDE3PMw@NKfg@%cm2aMz#k~yKkt+;Tu|`L1&L|s9mX#yozCQpe}OKA3Wz9c`F5#@2`uTsEfSf8+;YwQ+1Ko zqG#2*$f?Gm&VJ!L{-zNOd-v2uTD=cP-*D6qHQRjA*R}c~Lyf*;uOHf!GI&xMJfTEi zw++6c_y$iYgRd!#!KaL&^qU)lr^$G~5qzsL_|mE&@1~VQRjZ+53{AhuAAQxndMNix z{^)7D5q-sO4CVIvqo;tsW=Ap}ZdCr;h@P}JDxWqepEN2THz=bG%0~@Bb1a^+`}fuh ze}&y^RH*(6n^BQsR45I~$Af=XRu0Wyg>KW03Y&lMNyUi%+1{XhW>h$gGGr5Wy1$~% z3-K}_wSbsj6_{xzzFa4*!@jSd--*1tcmM1&tWkSmXU-SuonwQTDw#$kYEH!`{E-n% zjY=c(TO;yh6nZgiB_^}TC!V{im1GERK>gc2?>BF`GfU&mm$ z$~b=3aDLoS7BwQ!iRi|aPxc>!d;C+nF19`=s*@h6L-%`SBX2xsw)V(j`akr>Z?=pr z;MDjti#{41wHzKXlQH)14(gL-G6Rztn9RUr1|~BwnSsd+{B>tQnWiqGcgeWb6dqij z8Gxnlj*j*Kp7>yNlx`BweKs)xiWn&-ALQy(vLj3+~pZm@30AK&%fa~13O#>BRE&))h?Z|$ugON$qY

6D1M!SjLL{IKfcyFco-V`+NdE77Mw11e&vlhUP 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 diff --git a/resources/lib/deps/Cryptodome/Hash/_keccak.abi3.so b/resources/lib/deps/Cryptodome/Hash/_keccak.abi3.so deleted file mode 100755 index 4e494b0f7fbd6f9c44ba8c351c4bb93d1bb64098..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41632 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 Q@#IhKRr1AA0#T^=Utza+T>t<8 diff --git a/resources/lib/deps/Cryptodome/Hash/_poly1305.abi3.so b/resources/lib/deps/Cryptodome/Hash/_poly1305.abi3.so deleted file mode 100755 index 901b8c2c5b990d8b89554b147b370c9eae17f265..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33424 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 -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from Cryptodome.Util.py3compat import bchr, concat_buffers - -from Cryptodome.Util._raw_api import (VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr, c_ubyte) - -from Cryptodome.Util.number import long_to_bytes - -from Cryptodome.Hash.keccak import _raw_keccak_lib - - -def _left_encode(x): - """Left encode function as defined in NIST SP 800-185""" - - assert (x < (1 << 2040) and x >= 0) - - # Get number of bytes needed to represent this integer. - num = 1 if x == 0 else (x.bit_length() + 7) // 8 - - return bchr(num) + long_to_bytes(x) - - -def _right_encode(x): - """Right encode function as defined in NIST SP 800-185""" - - assert (x < (1 << 2040) and x >= 0) - - # Get number of bytes needed to represent this integer. - num = 1 if x == 0 else (x.bit_length() + 7) // 8 - - return long_to_bytes(x) + bchr(num) - - -def _encode_str(x): - """Encode string function as defined in NIST SP 800-185""" - - bitlen = len(x) * 8 - if bitlen >= (1 << 2040): - raise ValueError("String too large to encode in cSHAKE") - - return concat_buffers(_left_encode(bitlen), x) - - -def _bytepad(x, length): - """Zero pad byte string as defined in NIST SP 800-185""" - - to_pad = concat_buffers(_left_encode(length), x) - - # Note: this implementation works with byte aligned strings, - # hence no additional bit padding is needed at this point. - npad = (length - len(to_pad) % length) % length - - return to_pad + b'\x00' * npad - - -class cSHAKE_XOF(object): - """A cSHAKE hash object. - Do not instantiate directly. - Use the :func:`new` function. - """ - - def __init__(self, data, custom, capacity, function): - state = VoidPointer() - - if custom or function: - prefix_unpad = _encode_str(function) + _encode_str(custom) - prefix = _bytepad(prefix_unpad, (1600 - capacity)//8) - self._padding = 0x04 - else: - prefix = None - self._padding = 0x1F # for SHAKE - - result = _raw_keccak_lib.keccak_init(state.address_of(), - c_size_t(capacity//8), - c_ubyte(24)) - if result: - raise ValueError("Error %d while instantiating cSHAKE" - % result) - self._state = SmartPointer(state.get(), - _raw_keccak_lib.keccak_destroy) - self._is_squeezing = False - - if prefix: - self.update(prefix) - - if data: - self.update(data) - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Args: - data (byte string/byte array/memoryview): The next chunk of the message being hashed. - """ - - if self._is_squeezing: - raise TypeError("You cannot call 'update' after the first 'read'") - - result = _raw_keccak_lib.keccak_absorb(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data))) - if result: - raise ValueError("Error %d while updating %s state" - % (result, self.name)) - return self - - def read(self, length): - """ - Compute the next piece of XOF output. - - .. note:: - You cannot use :meth:`update` anymore after the first call to - :meth:`read`. - - Args: - length (integer): the amount of bytes this method must return - - :return: the next piece of XOF output (of the given length) - :rtype: byte string - """ - - self._is_squeezing = True - bfr = create_string_buffer(length) - result = _raw_keccak_lib.keccak_squeeze(self._state.get(), - bfr, - c_size_t(length), - c_ubyte(self._padding)) - if result: - raise ValueError("Error %d while extracting from %s" - % (result, self.name)) - - return get_raw_buffer(bfr) - - -def _new(data, custom, function): - # Use Keccak[256] - return cSHAKE_XOF(data, custom, 256, function) - - -def new(data=None, custom=None): - """Return a fresh instance of a cSHAKE128 object. - - Args: - data (bytes/bytearray/memoryview): - Optional. - The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`update`. - custom (bytes): - Optional. - A customization bytestring (``S`` in SP 800-185). - - :Return: A :class:`cSHAKE_XOF` object - """ - - # Use Keccak[256] - return cSHAKE_XOF(data, custom, 256, b'') diff --git a/resources/lib/deps/Cryptodome/Hash/cSHAKE128.pyi b/resources/lib/deps/Cryptodome/Hash/cSHAKE128.pyi deleted file mode 100644 index 1452fea..0000000 --- a/resources/lib/deps/Cryptodome/Hash/cSHAKE128.pyi +++ /dev/null @@ -1,14 +0,0 @@ -from typing import Union, Optional - -Buffer = Union[bytes, bytearray, memoryview] - -class cSHAKE_XOF(object): - def __init__(self, - data: Optional[Buffer] = ..., - function: Optional[bytes] = ..., - custom: Optional[bytes] = ...) -> None: ... - def update(self, data: Buffer) -> cSHAKE_XOF: ... - def read(self, length: int) -> bytes: ... - -def new(data: Optional[Buffer] = ..., - custom: Optional[Buffer] = ...) -> cSHAKE_XOF: ... diff --git a/resources/lib/deps/Cryptodome/Hash/cSHAKE256.py b/resources/lib/deps/Cryptodome/Hash/cSHAKE256.py deleted file mode 100644 index a5b8701..0000000 --- a/resources/lib/deps/Cryptodome/Hash/cSHAKE256.py +++ /dev/null @@ -1,56 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2021, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from Cryptodome.Util._raw_api import c_size_t -from Cryptodome.Hash.cSHAKE128 import cSHAKE_XOF - - -def _new(data, custom, function): - # Use Keccak[512] - return cSHAKE_XOF(data, custom, 512, function) - - -def new(data=None, custom=None): - """Return a fresh instance of a cSHAKE256 object. - - Args: - data (bytes/bytearray/memoryview): - The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`update`. - Optional. - custom (bytes): - Optional. - A customization bytestring (``S`` in SP 800-185). - - :Return: A :class:`cSHAKE_XOF` object - """ - - # Use Keccak[512] - return cSHAKE_XOF(data, custom, 512, b'') diff --git a/resources/lib/deps/Cryptodome/Hash/cSHAKE256.pyi b/resources/lib/deps/Cryptodome/Hash/cSHAKE256.pyi deleted file mode 100644 index b910bb6..0000000 --- a/resources/lib/deps/Cryptodome/Hash/cSHAKE256.pyi +++ /dev/null @@ -1,8 +0,0 @@ -from typing import Union, Optional - -from Cryptodome.Hash.cSHAKE128 import cSHAKE_XOF - -Buffer = Union[bytes, bytearray, memoryview] - -def new(data: Optional[Buffer] = ..., - custom: Optional[Buffer] = ...) -> cSHAKE_XOF: ... diff --git a/resources/lib/deps/Cryptodome/Hash/keccak.py b/resources/lib/deps/Cryptodome/Hash/keccak.py deleted file mode 100644 index f2af202..0000000 --- a/resources/lib/deps/Cryptodome/Hash/keccak.py +++ /dev/null @@ -1,181 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2015, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from Cryptodome.Util.py3compat import bord - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - VoidPointer, SmartPointer, - create_string_buffer, - get_raw_buffer, c_size_t, - c_uint8_ptr, c_ubyte) - -_raw_keccak_lib = load_pycryptodome_raw_lib("Cryptodome.Hash._keccak", - """ - int keccak_init(void **state, - size_t capacity_bytes, - uint8_t rounds); - int keccak_destroy(void *state); - int keccak_absorb(void *state, - const uint8_t *in, - size_t len); - int keccak_squeeze(const void *state, - uint8_t *out, - size_t len, - uint8_t padding); - int keccak_digest(void *state, - uint8_t *digest, - size_t len, - uint8_t padding); - int keccak_copy(const void *src, void *dst); - int keccak_reset(void *state); - """) - -class Keccak_Hash(object): - """A Keccak hash object. - Do not instantiate directly. - Use the :func:`new` function. - - :ivar digest_size: the size in bytes of the resulting hash - :vartype digest_size: integer - """ - - def __init__(self, data, digest_bytes, update_after_digest): - # The size of the resulting hash in bytes. - self.digest_size = digest_bytes - - self._update_after_digest = update_after_digest - self._digest_done = False - self._padding = 0x01 - - state = VoidPointer() - result = _raw_keccak_lib.keccak_init(state.address_of(), - c_size_t(self.digest_size * 2), - c_ubyte(24)) - if result: - raise ValueError("Error %d while instantiating keccak" % result) - self._state = SmartPointer(state.get(), - _raw_keccak_lib.keccak_destroy) - if data: - self.update(data) - - def update(self, data): - """Continue hashing of a message by consuming the next chunk of data. - - Args: - data (byte string/byte array/memoryview): The next chunk of the message being hashed. - """ - - if self._digest_done and not self._update_after_digest: - raise TypeError("You can only call 'digest' or 'hexdigest' on this object") - - result = _raw_keccak_lib.keccak_absorb(self._state.get(), - c_uint8_ptr(data), - c_size_t(len(data))) - if result: - raise ValueError("Error %d while updating keccak" % result) - return self - - def digest(self): - """Return the **binary** (non-printable) digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Binary form. - :rtype: byte string - """ - - self._digest_done = True - bfr = create_string_buffer(self.digest_size) - result = _raw_keccak_lib.keccak_digest(self._state.get(), - bfr, - c_size_t(self.digest_size), - c_ubyte(self._padding)) - if result: - raise ValueError("Error %d while squeezing keccak" % result) - - return get_raw_buffer(bfr) - - def hexdigest(self): - """Return the **printable** digest of the message that has been hashed so far. - - :return: The hash digest, computed over the data processed so far. - Hexadecimal encoded. - :rtype: string - """ - - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def new(self, **kwargs): - """Create a fresh Keccak hash object.""" - - if "digest_bytes" not in kwargs and "digest_bits" not in kwargs: - kwargs["digest_bytes"] = self.digest_size - - return new(**kwargs) - - -def new(**kwargs): - """Create a new hash object. - - Args: - data (bytes/bytearray/memoryview): - The very first chunk of the message to hash. - It is equivalent to an early call to :meth:`Keccak_Hash.update`. - digest_bytes (integer): - The size of the digest, in bytes (28, 32, 48, 64). - digest_bits (integer): - The size of the digest, in bits (224, 256, 384, 512). - update_after_digest (boolean): - Whether :meth:`Keccak.digest` can be followed by another - :meth:`Keccak.update` (default: ``False``). - - :Return: A :class:`Keccak_Hash` hash object - """ - - data = kwargs.pop("data", None) - update_after_digest = kwargs.pop("update_after_digest", False) - - digest_bytes = kwargs.pop("digest_bytes", None) - digest_bits = kwargs.pop("digest_bits", None) - if None not in (digest_bytes, digest_bits): - raise TypeError("Only one digest parameter must be provided") - if (None, None) == (digest_bytes, digest_bits): - raise TypeError("Digest size (bits, bytes) not provided") - if digest_bytes is not None: - if digest_bytes not in (28, 32, 48, 64): - raise ValueError("'digest_bytes' must be: 28, 32, 48 or 64") - else: - if digest_bits not in (224, 256, 384, 512): - raise ValueError("'digest_bytes' must be: 224, 256, 384 or 512") - digest_bytes = digest_bits // 8 - - if kwargs: - raise TypeError("Unknown parameters: " + str(kwargs)) - - return Keccak_Hash(data, digest_bytes, update_after_digest) diff --git a/resources/lib/deps/Cryptodome/Hash/keccak.pyi b/resources/lib/deps/Cryptodome/Hash/keccak.pyi deleted file mode 100644 index 844d256..0000000 --- a/resources/lib/deps/Cryptodome/Hash/keccak.pyi +++ /dev/null @@ -1,23 +0,0 @@ -from typing import Union, Any - -Buffer = Union[bytes, bytearray, memoryview] - -class Keccak_Hash(object): - digest_size: int - def __init__(self, - data: Buffer, - digest_bytes: int, - update_after_digest: bool) -> None: ... - def update(self, data: Buffer) -> Keccak_Hash: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def new(self, - data: Buffer = ..., - digest_bytes: int = ..., - digest_bits: int = ..., - update_after_digest: bool = ...) -> Keccak_Hash: ... - -def new(data: Buffer = ..., - digest_bytes: int = ..., - digest_bits: int = ..., - update_after_digest: bool = ...) -> Keccak_Hash: ... diff --git a/resources/lib/deps/Cryptodome/IO/PEM.py b/resources/lib/deps/Cryptodome/IO/PEM.py deleted file mode 100644 index 7655368..0000000 --- a/resources/lib/deps/Cryptodome/IO/PEM.py +++ /dev/null @@ -1,189 +0,0 @@ -# -# Util/PEM.py : Privacy Enhanced Mail utilities -# -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -__all__ = ['encode', 'decode'] - -import re -from binascii import a2b_base64, b2a_base64, hexlify, unhexlify - -from Cryptodome.Hash import MD5 -from Cryptodome.Util.Padding import pad, unpad -from Cryptodome.Cipher import DES, DES3, AES -from Cryptodome.Protocol.KDF import PBKDF1 -from Cryptodome.Random import get_random_bytes -from Cryptodome.Util.py3compat import tobytes, tostr - - -def encode(data, marker, passphrase=None, randfunc=None): - """Encode a piece of binary data into PEM format. - - Args: - data (byte string): - The piece of binary data to encode. - marker (string): - The marker for the PEM block (e.g. "PUBLIC KEY"). - Note that there is no official master list for all allowed markers. - Still, you can refer to the OpenSSL_ source code. - passphrase (byte string): - If given, the PEM block will be encrypted. The key is derived from - the passphrase. - randfunc (callable): - Random number generation function; it accepts an integer N and returns - a byte string of random data, N bytes long. If not given, a new one is - instantiated. - - Returns: - The PEM block, as a string. - - .. _OpenSSL: https://github.com/openssl/openssl/blob/master/include/openssl/pem.h - """ - - if randfunc is None: - randfunc = get_random_bytes - - out = "-----BEGIN %s-----\n" % marker - if passphrase: - # We only support 3DES for encryption - salt = randfunc(8) - key = PBKDF1(passphrase, salt, 16, 1, MD5) - key += PBKDF1(key + passphrase, salt, 8, 1, MD5) - objenc = DES3.new(key, DES3.MODE_CBC, salt) - out += "Proc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,%s\n\n" %\ - tostr(hexlify(salt).upper()) - # Encrypt with PKCS#7 padding - data = objenc.encrypt(pad(data, objenc.block_size)) - elif passphrase is not None: - raise ValueError("Empty password") - - # Each BASE64 line can take up to 64 characters (=48 bytes of data) - # b2a_base64 adds a new line character! - chunks = [tostr(b2a_base64(data[i:i + 48])) - for i in range(0, len(data), 48)] - out += "".join(chunks) - out += "-----END %s-----" % marker - return out - - -def _EVP_BytesToKey(data, salt, key_len): - d = [ b'' ] - m = (key_len + 15 ) // 16 - for _ in range(m): - nd = MD5.new(d[-1] + data + salt).digest() - d.append(nd) - return b"".join(d)[:key_len] - - -def decode(pem_data, passphrase=None): - """Decode a PEM block into binary. - - Args: - pem_data (string): - The PEM block. - passphrase (byte string): - If given and the PEM block is encrypted, - the key will be derived from the passphrase. - - Returns: - A tuple with the binary data, the marker string, and a boolean to - indicate if decryption was performed. - - Raises: - ValueError: if decoding fails, if the PEM file is encrypted and no passphrase has - been provided or if the passphrase is incorrect. - """ - - # Verify Pre-Encapsulation Boundary - r = re.compile(r"\s*-----BEGIN (.*)-----\s+") - m = r.match(pem_data) - if not m: - raise ValueError("Not a valid PEM pre boundary") - marker = m.group(1) - - # Verify Post-Encapsulation Boundary - r = re.compile(r"-----END (.*)-----\s*$") - m = r.search(pem_data) - if not m or m.group(1) != marker: - raise ValueError("Not a valid PEM post boundary") - - # Removes spaces and slit on lines - lines = pem_data.replace(" ", '').split() - - # Decrypts, if necessary - if lines[1].startswith('Proc-Type:4,ENCRYPTED'): - if not passphrase: - raise ValueError("PEM is encrypted, but no passphrase available") - DEK = lines[2].split(':') - if len(DEK) != 2 or DEK[0] != 'DEK-Info': - raise ValueError("PEM encryption format not supported.") - algo, salt = DEK[1].split(',') - salt = unhexlify(tobytes(salt)) - - padding = True - - if algo == "DES-CBC": - key = _EVP_BytesToKey(passphrase, salt, 8) - objdec = DES.new(key, DES.MODE_CBC, salt) - elif algo == "DES-EDE3-CBC": - key = _EVP_BytesToKey(passphrase, salt, 24) - objdec = DES3.new(key, DES3.MODE_CBC, salt) - elif algo == "AES-128-CBC": - key = _EVP_BytesToKey(passphrase, salt[:8], 16) - objdec = AES.new(key, AES.MODE_CBC, salt) - elif algo == "AES-192-CBC": - key = _EVP_BytesToKey(passphrase, salt[:8], 24) - objdec = AES.new(key, AES.MODE_CBC, salt) - elif algo == "AES-256-CBC": - key = _EVP_BytesToKey(passphrase, salt[:8], 32) - objdec = AES.new(key, AES.MODE_CBC, salt) - elif algo.lower() == "id-aes256-gcm": - key = _EVP_BytesToKey(passphrase, salt[:8], 32) - objdec = AES.new(key, AES.MODE_GCM, nonce=salt) - padding = False - else: - raise ValueError("Unsupport PEM encryption algorithm (%s)." % algo) - lines = lines[2:] - else: - objdec = None - - # Decode body - data = a2b_base64(''.join(lines[1:-1])) - enc_flag = False - if objdec: - if padding: - data = unpad(objdec.decrypt(data), objdec.block_size) - else: - # There is no tag, so we don't use decrypt_and_verify - data = objdec.decrypt(data) - enc_flag = True - - return (data, marker, enc_flag) diff --git a/resources/lib/deps/Cryptodome/IO/PEM.pyi b/resources/lib/deps/Cryptodome/IO/PEM.pyi deleted file mode 100644 index 2e324c4..0000000 --- a/resources/lib/deps/Cryptodome/IO/PEM.pyi +++ /dev/null @@ -1,10 +0,0 @@ -from typing import Tuple, Optional, Callable - -def encode(data: bytes, - marke: str, - passphrase: Optional[bytes] = ..., - randfunc: Optional[Callable[[int],bytes]] = ...) -> str: ... - - -def decode(pem_data: str, - passphrase: Optional[bytes] = ...) -> Tuple[bytes, str, bool]: ... diff --git a/resources/lib/deps/Cryptodome/IO/PKCS8.py b/resources/lib/deps/Cryptodome/IO/PKCS8.py deleted file mode 100644 index 3041545..0000000 --- a/resources/lib/deps/Cryptodome/IO/PKCS8.py +++ /dev/null @@ -1,226 +0,0 @@ -# -# PublicKey/PKCS8.py : PKCS#8 functions -# -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - - -from Cryptodome.Util.py3compat import * - -from Cryptodome.Util.asn1 import ( - DerNull, - DerSequence, - DerObjectId, - DerOctetString, - ) - -from Cryptodome.IO._PBES import PBES1, PBES2, PbesError - - -__all__ = ['wrap', 'unwrap'] - - -def wrap(private_key, key_oid, passphrase=None, protection=None, - prot_params=None, key_params=DerNull(), randfunc=None): - """Wrap a private key into a PKCS#8 blob (clear or encrypted). - - Args: - - private_key (bytes): - The private key encoded in binary form. The actual encoding is - algorithm specific. In most cases, it is DER. - - key_oid (string): - The object identifier (OID) of the private key to wrap. - It is a dotted string, like ``'1.2.840.113549.1.1.1'`` (for RSA keys) - or ``'1.2.840.10045.2.1'`` (for ECC keys). - - Keyword Args: - - passphrase (bytes or string): - The secret passphrase from which the wrapping key is derived. - Set it only if encryption is required. - - protection (string): - The identifier of the algorithm to use for securely wrapping the key. - Refer to :ref:`the encryption parameters` . - The default value is ``'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC'``. - - prot_params (dictionary): - Parameters for the key derivation function (KDF). - Refer to :ref:`the encryption parameters` . - - key_params (DER object or None): - The ``parameters`` field to use in the ``AlgorithmIdentifier`` - SEQUENCE. If ``None``, no ``parameters`` field will be added. - By default, the ASN.1 type ``NULL`` is used. - - randfunc (callable): - Random number generation function; it should accept a single integer - N and return a string of random data, N bytes long. - If not specified, a new RNG will be instantiated - from :mod:`Cryptodome.Random`. - - Returns: - bytes: The PKCS#8-wrapped private key (possibly encrypted). - """ - - # - # PrivateKeyInfo ::= SEQUENCE { - # version Version, - # privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, - # privateKey PrivateKey, - # attributes [0] IMPLICIT Attributes OPTIONAL - # } - # - if key_params is None: - algorithm = DerSequence([DerObjectId(key_oid)]) - else: - algorithm = DerSequence([DerObjectId(key_oid), key_params]) - - pk_info = DerSequence([ - 0, - algorithm, - DerOctetString(private_key) - ]) - pk_info_der = pk_info.encode() - - if passphrase is None: - return pk_info_der - - if not passphrase: - raise ValueError("Empty passphrase") - - # Encryption with PBES2 - passphrase = tobytes(passphrase) - if protection is None: - protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' - return PBES2.encrypt(pk_info_der, passphrase, - protection, prot_params, randfunc) - - -def unwrap(p8_private_key, passphrase=None): - """Unwrap a private key from a PKCS#8 blob (clear or encrypted). - - Args: - p8_private_key (bytes): - The private key wrapped into a PKCS#8 container, DER encoded. - - Keyword Args: - passphrase (byte string or string): - The passphrase to use to decrypt the blob (if it is encrypted). - - Return: - A tuple containing - - #. the algorithm identifier of the wrapped key (OID, dotted string) - #. the private key (bytes, DER encoded) - #. the associated parameters (bytes, DER encoded) or ``None`` - - Raises: - ValueError : if decoding fails - """ - - if passphrase: - passphrase = tobytes(passphrase) - - found = False - try: - p8_private_key = PBES1.decrypt(p8_private_key, passphrase) - found = True - except PbesError as e: - error_str = "PBES1[%s]" % str(e) - except ValueError: - error_str = "PBES1[Invalid]" - - if not found: - try: - p8_private_key = PBES2.decrypt(p8_private_key, passphrase) - found = True - except PbesError as e: - error_str += ",PBES2[%s]" % str(e) - except ValueError: - error_str += ",PBES2[Invalid]" - - if not found: - raise ValueError("Error decoding PKCS#8 (%s)" % error_str) - - pk_info = DerSequence().decode(p8_private_key, nr_elements=(2, 3, 4, 5)) - if len(pk_info) == 2 and not passphrase: - raise ValueError("Not a valid clear PKCS#8 structure " - "(maybe it is encrypted?)") - - # RFC5208, PKCS#8, version is v1(0) - # - # PrivateKeyInfo ::= SEQUENCE { - # version Version, - # privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, - # privateKey PrivateKey, - # attributes [0] IMPLICIT Attributes OPTIONAL - # } - # - # RFC5915, Asymmetric Key Package, version is v2(1) - # - # OneAsymmetricKey ::= SEQUENCE { - # version Version, - # privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, - # privateKey PrivateKey, - # attributes [0] Attributes OPTIONAL, - # ..., - # [[2: publicKey [1] PublicKey OPTIONAL ]], - # ... - # } - - if pk_info[0] == 0: - if len(pk_info) not in (3, 4): - raise ValueError("Not a valid PrivateKeyInfo SEQUENCE") - elif pk_info[0] == 1: - if len(pk_info) not in (3, 4, 5): - raise ValueError("Not a valid PrivateKeyInfo SEQUENCE") - else: - raise ValueError("Not a valid PrivateKeyInfo SEQUENCE") - - algo = DerSequence().decode(pk_info[1], nr_elements=(1, 2)) - algo_oid = DerObjectId().decode(algo[0]).value - if len(algo) == 1: - algo_params = None - else: - try: - DerNull().decode(algo[1]) - algo_params = None - except: - algo_params = algo[1] - - # PrivateKey ::= OCTET STRING - private_key = DerOctetString().decode(pk_info[2]).payload - - # We ignore attributes and (for v2 only) publickey - - return (algo_oid, private_key, algo_params) diff --git a/resources/lib/deps/Cryptodome/IO/PKCS8.pyi b/resources/lib/deps/Cryptodome/IO/PKCS8.pyi deleted file mode 100644 index c8d0c10..0000000 --- a/resources/lib/deps/Cryptodome/IO/PKCS8.pyi +++ /dev/null @@ -1,17 +0,0 @@ -from typing import Tuple, Optional, Union, Callable -from typing_extensions import NotRequired - -from Cryptodome.Util.asn1 import DerObject -from Cryptodome.IO._PBES import ProtParams - - -def wrap(private_key: bytes, - key_oid: str, - passphrase: Union[bytes, str] = ..., - protection: str = ..., - prot_params: Optional[ProtParams] = ..., - key_params: Optional[DerObject] = ..., - randfunc: Optional[Callable[[int], str]] = ...) -> bytes: ... - - -def unwrap(p8_private_key: bytes, passphrase: Optional[Union[bytes, str]] = ...) -> Tuple[str, bytes, Optional[bytes]]: ... diff --git a/resources/lib/deps/Cryptodome/IO/_PBES.py b/resources/lib/deps/Cryptodome/IO/_PBES.py deleted file mode 100644 index 75d8cde..0000000 --- a/resources/lib/deps/Cryptodome/IO/_PBES.py +++ /dev/null @@ -1,546 +0,0 @@ -# -# PublicKey/_PBES.py : Password-Based Encryption functions -# -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import re - -from Cryptodome import Hash -from Cryptodome import Random -from Cryptodome.Util.asn1 import ( - DerSequence, DerOctetString, - DerObjectId, DerInteger, - ) - -from Cryptodome.Cipher import AES -from Cryptodome.Util.Padding import pad, unpad -from Cryptodome.Protocol.KDF import PBKDF1, PBKDF2, scrypt - -_OID_PBE_WITH_MD5_AND_DES_CBC = "1.2.840.113549.1.5.3" -_OID_PBE_WITH_MD5_AND_RC2_CBC = "1.2.840.113549.1.5.6" -_OID_PBE_WITH_SHA1_AND_DES_CBC = "1.2.840.113549.1.5.10" -_OID_PBE_WITH_SHA1_AND_RC2_CBC = "1.2.840.113549.1.5.11" - -_OID_PBES2 = "1.2.840.113549.1.5.13" - -_OID_PBKDF2 = "1.2.840.113549.1.5.12" -_OID_SCRYPT = "1.3.6.1.4.1.11591.4.11" - -_OID_HMAC_SHA1 = "1.2.840.113549.2.7" - -_OID_DES_EDE3_CBC = "1.2.840.113549.3.7" -_OID_AES128_CBC = "2.16.840.1.101.3.4.1.2" -_OID_AES192_CBC = "2.16.840.1.101.3.4.1.22" -_OID_AES256_CBC = "2.16.840.1.101.3.4.1.42" -_OID_AES128_GCM = "2.16.840.1.101.3.4.1.6" -_OID_AES192_GCM = "2.16.840.1.101.3.4.1.26" -_OID_AES256_GCM = "2.16.840.1.101.3.4.1.46" - -class PbesError(ValueError): - pass - -# These are the ASN.1 definitions used by the PBES1/2 logic: -# -# EncryptedPrivateKeyInfo ::= SEQUENCE { -# encryptionAlgorithm EncryptionAlgorithmIdentifier, -# encryptedData EncryptedData -# } -# -# EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier -# -# EncryptedData ::= OCTET STRING -# -# AlgorithmIdentifier ::= SEQUENCE { -# algorithm OBJECT IDENTIFIER, -# parameters ANY DEFINED BY algorithm OPTIONAL -# } -# -# PBEParameter ::= SEQUENCE { -# salt OCTET STRING (SIZE(8)), -# iterationCount INTEGER -# } -# -# PBES2-params ::= SEQUENCE { -# keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}}, -# encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} -# } -# -# PBKDF2-params ::= SEQUENCE { -# salt CHOICE { -# specified OCTET STRING, -# otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}} -# }, -# iterationCount INTEGER (1..MAX), -# keyLength INTEGER (1..MAX) OPTIONAL, -# prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1 -# } -# -# PBKDF2-PRFs ALGORITHM-IDENTIFIER ::= { -# {NULL IDENTIFIED BY id-hmacWithSHA1}, -# {NULL IDENTIFIED BY id-hmacWithSHA224}, -# {NULL IDENTIFIED BY id-hmacWithSHA256}, -# {NULL IDENTIFIED BY id-hmacWithSHA384}, -# {NULL IDENTIFIED BY id-hmacWithSHA512}, -# {NULL IDENTIFIED BY id-hmacWithSHA512-224}, -# {NULL IDENTIFIED BY id-hmacWithSHA512-256}, -# ... -# } -# scrypt-params ::= SEQUENCE { -# salt OCTET STRING, -# costParameter INTEGER (1..MAX), -# blockSize INTEGER (1..MAX), -# parallelizationParameter INTEGER (1..MAX), -# keyLength INTEGER (1..MAX) OPTIONAL -# } - - -class PBES1(object): - """Deprecated encryption scheme with password-based key derivation - (originally defined in PKCS#5 v1.5, but still present in `v2.0`__). - - .. __: http://www.ietf.org/rfc/rfc2898.txt - """ - - @staticmethod - def decrypt(data, passphrase): - """Decrypt a piece of data using a passphrase and *PBES1*. - - The algorithm to use is automatically detected. - - :Parameters: - data : byte string - The piece of data to decrypt. - passphrase : byte string - The passphrase to use for decrypting the data. - :Returns: - The decrypted data, as a binary string. - """ - - enc_private_key_info = DerSequence().decode(data) - encrypted_algorithm = DerSequence().decode(enc_private_key_info[0]) - encrypted_data = DerOctetString().decode(enc_private_key_info[1]).payload - - pbe_oid = DerObjectId().decode(encrypted_algorithm[0]).value - cipher_params = {} - if pbe_oid == _OID_PBE_WITH_MD5_AND_DES_CBC: - # PBE_MD5_DES_CBC - from Cryptodome.Hash import MD5 - from Cryptodome.Cipher import DES - hashmod = MD5 - module = DES - elif pbe_oid == _OID_PBE_WITH_MD5_AND_RC2_CBC: - # PBE_MD5_RC2_CBC - from Cryptodome.Hash import MD5 - from Cryptodome.Cipher import ARC2 - hashmod = MD5 - module = ARC2 - cipher_params['effective_keylen'] = 64 - elif pbe_oid == _OID_PBE_WITH_SHA1_AND_DES_CBC: - # PBE_SHA1_DES_CBC - from Cryptodome.Hash import SHA1 - from Cryptodome.Cipher import DES - hashmod = SHA1 - module = DES - elif pbe_oid == _OID_PBE_WITH_SHA1_AND_RC2_CBC: - # PBE_SHA1_RC2_CBC - from Cryptodome.Hash import SHA1 - from Cryptodome.Cipher import ARC2 - hashmod = SHA1 - module = ARC2 - cipher_params['effective_keylen'] = 64 - else: - raise PbesError("Unknown OID for PBES1") - - pbe_params = DerSequence().decode(encrypted_algorithm[1], nr_elements=2) - salt = DerOctetString().decode(pbe_params[0]).payload - iterations = pbe_params[1] - - key_iv = PBKDF1(passphrase, salt, 16, iterations, hashmod) - key, iv = key_iv[:8], key_iv[8:] - - cipher = module.new(key, module.MODE_CBC, iv, **cipher_params) - pt = cipher.decrypt(encrypted_data) - return unpad(pt, cipher.block_size) - - -class PBES2(object): - """Encryption scheme with password-based key derivation - (defined in `PKCS#5 v2.0`__). - - .. __: http://www.ietf.org/rfc/rfc2898.txt.""" - - @staticmethod - def encrypt(data, passphrase, protection, prot_params=None, randfunc=None): - """Encrypt a piece of data using a passphrase and *PBES2*. - - :Parameters: - data : byte string - The piece of data to encrypt. - passphrase : byte string - The passphrase to use for encrypting the data. - protection : string - The identifier of the encryption algorithm to use. - The default value is '``PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC``'. - prot_params : dictionary - Parameters of the protection algorithm. - - +------------------+-----------------------------------------------+ - | Key | Description | - +==================+===============================================+ - | iteration_count | The KDF algorithm is repeated several times to| - | | slow down brute force attacks on passwords | - | | (called *N* or CPU/memory cost in scrypt). | - | | | - | | The default value for PBKDF2 is 1 000. | - | | The default value for scrypt is 16 384. | - +------------------+-----------------------------------------------+ - | salt_size | Salt is used to thwart dictionary and rainbow | - | | attacks on passwords. The default value is 8 | - | | bytes. | - +------------------+-----------------------------------------------+ - | block_size | *(scrypt only)* Memory-cost (r). The default | - | | value is 8. | - +------------------+-----------------------------------------------+ - | parallelization | *(scrypt only)* CPU-cost (p). The default | - | | value is 1. | - +------------------+-----------------------------------------------+ - - - randfunc : callable - Random number generation function; it should accept - a single integer N and return a string of random data, - N bytes long. If not specified, a new RNG will be - instantiated from ``Cryptodome.Random``. - - :Returns: - The encrypted data, as a binary string. - """ - - if prot_params is None: - prot_params = {} - - if randfunc is None: - randfunc = Random.new().read - - pattern = re.compile(r'^(PBKDF2WithHMAC-([0-9A-Z-]+)|scrypt)And([0-9A-Z-]+)$') - res = pattern.match(protection) - if res is None: - raise ValueError("Unknown protection %s" % protection) - - if protection.startswith("PBKDF"): - pbkdf = "pbkdf2" - pbkdf2_hmac_algo = res.group(2) - enc_algo = res.group(3) - else: - pbkdf = "scrypt" - enc_algo = res.group(3) - - aead = False - if enc_algo == 'DES-EDE3-CBC': - from Cryptodome.Cipher import DES3 - key_size = 24 - module = DES3 - cipher_mode = DES3.MODE_CBC - enc_oid = _OID_DES_EDE3_CBC - enc_param = {'iv': randfunc(8)} - elif enc_algo == 'AES128-CBC': - key_size = 16 - module = AES - cipher_mode = AES.MODE_CBC - enc_oid = _OID_AES128_CBC - enc_param = {'iv': randfunc(16)} - elif enc_algo == 'AES192-CBC': - key_size = 24 - module = AES - cipher_mode = AES.MODE_CBC - enc_oid = _OID_AES192_CBC - enc_param = {'iv': randfunc(16)} - elif enc_algo == 'AES256-CBC': - key_size = 32 - module = AES - cipher_mode = AES.MODE_CBC - enc_oid = _OID_AES256_CBC - enc_param = {'iv': randfunc(16)} - elif enc_algo == 'AES128-GCM': - key_size = 16 - module = AES - cipher_mode = AES.MODE_GCM - enc_oid = _OID_AES128_GCM - enc_param = {'nonce': randfunc(12)} - aead = True - elif enc_algo == 'AES192-GCM': - key_size = 24 - module = AES - cipher_mode = AES.MODE_GCM - enc_oid = _OID_AES192_GCM - enc_param = {'nonce': randfunc(12)} - aead = True - elif enc_algo == 'AES256-GCM': - key_size = 32 - module = AES - cipher_mode = AES.MODE_GCM - enc_oid = _OID_AES256_GCM - enc_param = {'nonce': randfunc(12)} - aead = True - else: - raise ValueError("Unknown encryption mode '%s'" % enc_algo) - - iv_nonce = list(enc_param.values())[0] - salt = randfunc(prot_params.get("salt_size", 8)) - - # Derive key from password - if pbkdf == 'pbkdf2': - - count = prot_params.get("iteration_count", 1000) - digestmod = Hash.new(pbkdf2_hmac_algo) - - key = PBKDF2(passphrase, - salt, - key_size, - count, - hmac_hash_module=digestmod) - - pbkdf2_params = DerSequence([ - DerOctetString(salt), - DerInteger(count) - ]) - - if pbkdf2_hmac_algo != 'SHA1': - try: - hmac_oid = Hash.HMAC.new(b'', digestmod=digestmod).oid - except KeyError: - raise ValueError("No OID for HMAC hash algorithm") - pbkdf2_params.append(DerSequence([DerObjectId(hmac_oid)])) - - kdf_info = DerSequence([ - DerObjectId(_OID_PBKDF2), # PBKDF2 - pbkdf2_params - ]) - - elif pbkdf == 'scrypt': - - count = prot_params.get("iteration_count", 16384) - scrypt_r = prot_params.get('block_size', 8) - scrypt_p = prot_params.get('parallelization', 1) - key = scrypt(passphrase, salt, key_size, - count, scrypt_r, scrypt_p) - kdf_info = DerSequence([ - DerObjectId(_OID_SCRYPT), # scrypt - DerSequence([ - DerOctetString(salt), - DerInteger(count), - DerInteger(scrypt_r), - DerInteger(scrypt_p) - ]) - ]) - - else: - raise ValueError("Unknown KDF " + res.group(1)) - - # Create cipher and use it - cipher = module.new(key, cipher_mode, **enc_param) - if aead: - ct, tag = cipher.encrypt_and_digest(data) - encrypted_data = ct + tag - else: - encrypted_data = cipher.encrypt(pad(data, cipher.block_size)) - enc_info = DerSequence([ - DerObjectId(enc_oid), - DerOctetString(iv_nonce) - ]) - - # Result - enc_private_key_info = DerSequence([ - # encryptionAlgorithm - DerSequence([ - DerObjectId(_OID_PBES2), - DerSequence([ - kdf_info, - enc_info - ]), - ]), - DerOctetString(encrypted_data) - ]) - return enc_private_key_info.encode() - - @staticmethod - def decrypt(data, passphrase): - """Decrypt a piece of data using a passphrase and *PBES2*. - - The algorithm to use is automatically detected. - - :Parameters: - data : byte string - The piece of data to decrypt. - passphrase : byte string - The passphrase to use for decrypting the data. - :Returns: - The decrypted data, as a binary string. - """ - - enc_private_key_info = DerSequence().decode(data, nr_elements=2) - enc_algo = DerSequence().decode(enc_private_key_info[0]) - encrypted_data = DerOctetString().decode(enc_private_key_info[1]).payload - - pbe_oid = DerObjectId().decode(enc_algo[0]).value - if pbe_oid != _OID_PBES2: - raise PbesError("Not a PBES2 object") - - pbes2_params = DerSequence().decode(enc_algo[1], nr_elements=2) - - # Key Derivation Function selection - kdf_info = DerSequence().decode(pbes2_params[0], nr_elements=2) - kdf_oid = DerObjectId().decode(kdf_info[0]).value - - kdf_key_length = None - - # We only support PBKDF2 or scrypt - if kdf_oid == _OID_PBKDF2: - - pbkdf2_params = DerSequence().decode(kdf_info[1], nr_elements=(2, 3, 4)) - salt = DerOctetString().decode(pbkdf2_params[0]).payload - iteration_count = pbkdf2_params[1] - - left = len(pbkdf2_params) - 2 - idx = 2 - - if left > 0: - try: - # Check if it's an INTEGER - kdf_key_length = pbkdf2_params[idx] - 0 - left -= 1 - idx += 1 - except TypeError: - # keyLength is not present - pass - - # Default is HMAC-SHA1 - pbkdf2_prf_oid = _OID_HMAC_SHA1 - if left > 0: - pbkdf2_prf_algo_id = DerSequence().decode(pbkdf2_params[idx]) - pbkdf2_prf_oid = DerObjectId().decode(pbkdf2_prf_algo_id[0]).value - - elif kdf_oid == _OID_SCRYPT: - - scrypt_params = DerSequence().decode(kdf_info[1], nr_elements=(4, 5)) - salt = DerOctetString().decode(scrypt_params[0]).payload - iteration_count, scrypt_r, scrypt_p = [scrypt_params[x] - for x in (1, 2, 3)] - if len(scrypt_params) > 4: - kdf_key_length = scrypt_params[4] - else: - kdf_key_length = None - else: - raise PbesError("Unsupported PBES2 KDF") - - # Cipher selection - enc_info = DerSequence().decode(pbes2_params[1]) - enc_oid = DerObjectId().decode(enc_info[0]).value - - aead = False - if enc_oid == _OID_DES_EDE3_CBC: - # DES_EDE3_CBC - from Cryptodome.Cipher import DES3 - module = DES3 - cipher_mode = DES3.MODE_CBC - key_size = 24 - cipher_param = 'iv' - elif enc_oid == _OID_AES128_CBC: - module = AES - cipher_mode = AES.MODE_CBC - key_size = 16 - cipher_param = 'iv' - elif enc_oid == _OID_AES192_CBC: - module = AES - cipher_mode = AES.MODE_CBC - key_size = 24 - cipher_param = 'iv' - elif enc_oid == _OID_AES256_CBC: - module = AES - cipher_mode = AES.MODE_CBC - key_size = 32 - cipher_param = 'iv' - elif enc_oid == _OID_AES128_GCM: - module = AES - cipher_mode = AES.MODE_GCM - key_size = 16 - cipher_param = 'nonce' - aead = True - elif enc_oid == _OID_AES192_GCM: - module = AES - cipher_mode = AES.MODE_GCM - key_size = 24 - cipher_param = 'nonce' - aead = True - elif enc_oid == _OID_AES256_GCM: - module = AES - cipher_mode = AES.MODE_GCM - key_size = 32 - cipher_param = 'nonce' - aead = True - else: - raise PbesError("Unsupported PBES2 cipher " + enc_algo) - - if kdf_key_length and kdf_key_length != key_size: - raise PbesError("Mismatch between PBES2 KDF parameters" - " and selected cipher") - - iv_nonce = DerOctetString().decode(enc_info[1]).payload - - # Create cipher - if kdf_oid == _OID_PBKDF2: - - try: - hmac_hash_module_oid = Hash.HMAC._hmac2hash_oid[pbkdf2_prf_oid] - except KeyError: - raise PbesError("Unsupported HMAC %s" % pbkdf2_prf_oid) - hmac_hash_module = Hash.new(hmac_hash_module_oid) - - key = PBKDF2(passphrase, salt, key_size, iteration_count, - hmac_hash_module=hmac_hash_module) - else: - key = scrypt(passphrase, salt, key_size, iteration_count, - scrypt_r, scrypt_p) - cipher = module.new(key, cipher_mode, **{cipher_param:iv_nonce}) - - # Decrypt data - if len(encrypted_data) < cipher.block_size: - raise ValueError("Too little data to decrypt") - - if aead: - tag_len = cipher.block_size - pt = cipher.decrypt_and_verify(encrypted_data[:-tag_len], - encrypted_data[-tag_len:]) - else: - pt_padded = cipher.decrypt(encrypted_data) - pt = unpad(pt_padded, cipher.block_size) - - return pt diff --git a/resources/lib/deps/Cryptodome/IO/_PBES.pyi b/resources/lib/deps/Cryptodome/IO/_PBES.pyi deleted file mode 100644 index 0673364..0000000 --- a/resources/lib/deps/Cryptodome/IO/_PBES.pyi +++ /dev/null @@ -1,26 +0,0 @@ -from typing import Optional, Callable, TypedDict -from typing_extensions import NotRequired - -class PbesError(ValueError): - ... - -class PBES1(object): - @staticmethod - def decrypt(data: bytes, passphrase: bytes) -> bytes: ... - -class ProtParams(TypedDict): - iteration_count: NotRequired[int] - salt_size: NotRequired[int] - block_size: NotRequired[int] - parallelization: NotRequired[int] - -class PBES2(object): - @staticmethod - def encrypt(data: bytes, - passphrase: bytes, - protection: str, - prot_params: Optional[ProtParams] = ..., - randfunc: Optional[Callable[[int],bytes]] = ...) -> bytes: ... - - @staticmethod - def decrypt(data:bytes, passphrase: bytes) -> bytes: ... diff --git a/resources/lib/deps/Cryptodome/IO/__init__.py b/resources/lib/deps/Cryptodome/IO/__init__.py deleted file mode 100644 index 85a0d0b..0000000 --- a/resources/lib/deps/Cryptodome/IO/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -__all__ = ['PEM', 'PKCS8'] diff --git a/resources/lib/deps/Cryptodome/Math/Numbers.py b/resources/lib/deps/Cryptodome/Math/Numbers.py deleted file mode 100644 index c9ff848..0000000 --- a/resources/lib/deps/Cryptodome/Math/Numbers.py +++ /dev/null @@ -1,42 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -__all__ = ["Integer"] - -try: - from Cryptodome.Math._IntegerGMP import IntegerGMP as Integer - from Cryptodome.Math._IntegerGMP import implementation as _implementation -except (ImportError, OSError, AttributeError): - try: - from Cryptodome.Math._IntegerCustom import IntegerCustom as Integer - from Cryptodome.Math._IntegerCustom import implementation as _implementation - except (ImportError, OSError): - from Cryptodome.Math._IntegerNative import IntegerNative as Integer - _implementation = {} diff --git a/resources/lib/deps/Cryptodome/Math/Numbers.pyi b/resources/lib/deps/Cryptodome/Math/Numbers.pyi deleted file mode 100644 index b0206ca..0000000 --- a/resources/lib/deps/Cryptodome/Math/Numbers.pyi +++ /dev/null @@ -1,2 +0,0 @@ -from Cryptodome.Math._IntegerBase import IntegerBase as Integer -__all__ = ['Integer'] diff --git a/resources/lib/deps/Cryptodome/Math/Primality.py b/resources/lib/deps/Cryptodome/Math/Primality.py deleted file mode 100644 index 33814fa..0000000 --- a/resources/lib/deps/Cryptodome/Math/Primality.py +++ /dev/null @@ -1,369 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -"""Functions to create and test prime numbers. - -:undocumented: __package__ -""" - -from Cryptodome import Random -from Cryptodome.Math.Numbers import Integer - -from Cryptodome.Util.py3compat import iter_range - -COMPOSITE = 0 -PROBABLY_PRIME = 1 - - -def miller_rabin_test(candidate, iterations, randfunc=None): - """Perform a Miller-Rabin primality test on an integer. - - The test is specified in Section C.3.1 of `FIPS PUB 186-4`__. - - :Parameters: - candidate : integer - The number to test for primality. - iterations : integer - The maximum number of iterations to perform before - declaring a candidate a probable prime. - randfunc : callable - An RNG function where bases are taken from. - - :Returns: - ``Primality.COMPOSITE`` or ``Primality.PROBABLY_PRIME``. - - .. __: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf - """ - - if not isinstance(candidate, Integer): - candidate = Integer(candidate) - - if candidate in (1, 2, 3, 5): - return PROBABLY_PRIME - - if candidate.is_even(): - return COMPOSITE - - one = Integer(1) - minus_one = Integer(candidate - 1) - - if randfunc is None: - randfunc = Random.new().read - - # Step 1 and 2 - m = Integer(minus_one) - a = 0 - while m.is_even(): - m >>= 1 - a += 1 - - # Skip step 3 - - # Step 4 - for i in iter_range(iterations): - - # Step 4.1-2 - base = 1 - while base in (one, minus_one): - base = Integer.random_range(min_inclusive=2, - max_inclusive=candidate - 2, - randfunc=randfunc) - assert(2 <= base <= candidate - 2) - - # Step 4.3-4.4 - z = pow(base, m, candidate) - if z in (one, minus_one): - continue - - # Step 4.5 - for j in iter_range(1, a): - z = pow(z, 2, candidate) - if z == minus_one: - break - if z == one: - return COMPOSITE - else: - return COMPOSITE - - # Step 5 - return PROBABLY_PRIME - - -def lucas_test(candidate): - """Perform a Lucas primality test on an integer. - - The test is specified in Section C.3.3 of `FIPS PUB 186-4`__. - - :Parameters: - candidate : integer - The number to test for primality. - - :Returns: - ``Primality.COMPOSITE`` or ``Primality.PROBABLY_PRIME``. - - .. __: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf - """ - - if not isinstance(candidate, Integer): - candidate = Integer(candidate) - - # Step 1 - if candidate in (1, 2, 3, 5): - return PROBABLY_PRIME - if candidate.is_even() or candidate.is_perfect_square(): - return COMPOSITE - - # Step 2 - def alternate(): - value = 5 - while True: - yield value - if value > 0: - value += 2 - else: - value -= 2 - value = -value - - for D in alternate(): - if candidate in (D, -D): - continue - js = Integer.jacobi_symbol(D, candidate) - if js == 0: - return COMPOSITE - if js == -1: - break - # Found D. P=1 and Q=(1-D)/4 (note that Q is guaranteed to be an integer) - - # Step 3 - # This is \delta(n) = n - jacobi(D/n) - K = candidate + 1 - # Step 4 - r = K.size_in_bits() - 1 - # Step 5 - # U_1=1 and V_1=P - U_i = Integer(1) - V_i = Integer(1) - U_temp = Integer(0) - V_temp = Integer(0) - # Step 6 - for i in iter_range(r - 1, -1, -1): - # Square - # U_temp = U_i * V_i % candidate - U_temp.set(U_i) - U_temp *= V_i - U_temp %= candidate - # V_temp = (((V_i ** 2 + (U_i ** 2 * D)) * K) >> 1) % candidate - V_temp.set(U_i) - V_temp *= U_i - V_temp *= D - V_temp.multiply_accumulate(V_i, V_i) - if V_temp.is_odd(): - V_temp += candidate - V_temp >>= 1 - V_temp %= candidate - # Multiply - if K.get_bit(i): - # U_i = (((U_temp + V_temp) * K) >> 1) % candidate - U_i.set(U_temp) - U_i += V_temp - if U_i.is_odd(): - U_i += candidate - U_i >>= 1 - U_i %= candidate - # V_i = (((V_temp + U_temp * D) * K) >> 1) % candidate - V_i.set(V_temp) - V_i.multiply_accumulate(U_temp, D) - if V_i.is_odd(): - V_i += candidate - V_i >>= 1 - V_i %= candidate - else: - U_i.set(U_temp) - V_i.set(V_temp) - # Step 7 - if U_i == 0: - return PROBABLY_PRIME - return COMPOSITE - - -from Cryptodome.Util.number import sieve_base as _sieve_base_large -## The optimal number of small primes to use for the sieve -## is probably dependent on the platform and the candidate size -_sieve_base = set(_sieve_base_large[:100]) - - -def test_probable_prime(candidate, randfunc=None): - """Test if a number is prime. - - A number is qualified as prime if it passes a certain - number of Miller-Rabin tests (dependent on the size - of the number, but such that probability of a false - positive is less than 10^-30) and a single Lucas test. - - For instance, a 1024-bit candidate will need to pass - 4 Miller-Rabin tests. - - :Parameters: - candidate : integer - The number to test for primality. - randfunc : callable - The routine to draw random bytes from to select Miller-Rabin bases. - :Returns: - ``PROBABLE_PRIME`` if the number if prime with very high probability. - ``COMPOSITE`` if the number is a composite. - For efficiency reasons, ``COMPOSITE`` is also returned for small primes. - """ - - if randfunc is None: - randfunc = Random.new().read - - if not isinstance(candidate, Integer): - candidate = Integer(candidate) - - # First, check trial division by the smallest primes - if int(candidate) in _sieve_base: - return PROBABLY_PRIME - try: - map(candidate.fail_if_divisible_by, _sieve_base) - except ValueError: - return COMPOSITE - - # These are the number of Miller-Rabin iterations s.t. p(k, t) < 1E-30, - # with p(k, t) being the probability that a randomly chosen k-bit number - # is composite but still survives t MR iterations. - mr_ranges = ((220, 30), (280, 20), (390, 15), (512, 10), - (620, 7), (740, 6), (890, 5), (1200, 4), - (1700, 3), (3700, 2)) - - bit_size = candidate.size_in_bits() - try: - mr_iterations = list(filter(lambda x: bit_size < x[0], - mr_ranges))[0][1] - except IndexError: - mr_iterations = 1 - - if miller_rabin_test(candidate, mr_iterations, - randfunc=randfunc) == COMPOSITE: - return COMPOSITE - if lucas_test(candidate) == COMPOSITE: - return COMPOSITE - return PROBABLY_PRIME - - -def generate_probable_prime(**kwargs): - """Generate a random probable prime. - - The prime will not have any specific properties - (e.g. it will not be a *strong* prime). - - Random numbers are evaluated for primality until one - passes all tests, consisting of a certain number of - Miller-Rabin tests with random bases followed by - a single Lucas test. - - The number of Miller-Rabin iterations is chosen such that - the probability that the output number is a non-prime is - less than 1E-30 (roughly 2^{-100}). - - This approach is compliant to `FIPS PUB 186-4`__. - - :Keywords: - exact_bits : integer - The desired size in bits of the probable prime. - It must be at least 160. - randfunc : callable - An RNG function where candidate primes are taken from. - prime_filter : callable - A function that takes an Integer as parameter and returns - True if the number can be passed to further primality tests, - False if it should be immediately discarded. - - :Return: - A probable prime in the range 2^exact_bits > p > 2^(exact_bits-1). - - .. __: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf - """ - - exact_bits = kwargs.pop("exact_bits", None) - randfunc = kwargs.pop("randfunc", None) - prime_filter = kwargs.pop("prime_filter", lambda x: True) - if kwargs: - raise ValueError("Unknown parameters: " + kwargs.keys()) - - if exact_bits is None: - raise ValueError("Missing exact_bits parameter") - if exact_bits < 160: - raise ValueError("Prime number is not big enough.") - - if randfunc is None: - randfunc = Random.new().read - - result = COMPOSITE - while result == COMPOSITE: - candidate = Integer.random(exact_bits=exact_bits, - randfunc=randfunc) | 1 - if not prime_filter(candidate): - continue - result = test_probable_prime(candidate, randfunc) - return candidate - - -def generate_probable_safe_prime(**kwargs): - """Generate a random, probable safe prime. - - Note this operation is much slower than generating a simple prime. - - :Keywords: - exact_bits : integer - The desired size in bits of the probable safe prime. - randfunc : callable - An RNG function where candidate primes are taken from. - - :Return: - A probable safe prime in the range - 2^exact_bits > p > 2^(exact_bits-1). - """ - - exact_bits = kwargs.pop("exact_bits", None) - randfunc = kwargs.pop("randfunc", None) - if kwargs: - raise ValueError("Unknown parameters: " + kwargs.keys()) - - if randfunc is None: - randfunc = Random.new().read - - result = COMPOSITE - while result == COMPOSITE: - q = generate_probable_prime(exact_bits=exact_bits - 1, randfunc=randfunc) - candidate = q * 2 + 1 - if candidate.size_in_bits() != exact_bits: - continue - result = test_probable_prime(candidate, randfunc=randfunc) - return candidate diff --git a/resources/lib/deps/Cryptodome/Math/Primality.pyi b/resources/lib/deps/Cryptodome/Math/Primality.pyi deleted file mode 100644 index 7813483..0000000 --- a/resources/lib/deps/Cryptodome/Math/Primality.pyi +++ /dev/null @@ -1,18 +0,0 @@ -from typing import Callable, Optional, Union, Set - -PrimeResult = int - -COMPOSITE: PrimeResult -PROBABLY_PRIME: PrimeResult - -def miller_rabin_test(candidate: int, iterations: int, randfunc: Optional[Callable[[int],bytes]]=None) -> PrimeResult: ... -def lucas_test(candidate: int) -> PrimeResult: ... -_sieve_base: Set[int] -def test_probable_prime(candidate: int, randfunc: Optional[Callable[[int],bytes]]=None) -> PrimeResult: ... -def generate_probable_prime(*, - exact_bits: int = ..., - randfunc: Callable[[int],bytes] = ..., - prime_filter: Callable[[int],bool] = ...) -> int: ... -def generate_probable_safe_prime(*, - exact_bits: int = ..., - randfunc: Callable[[int],bytes] = ...) -> int: ... diff --git a/resources/lib/deps/Cryptodome/Math/_IntegerBase.py b/resources/lib/deps/Cryptodome/Math/_IntegerBase.py deleted file mode 100644 index 03dd591..0000000 --- a/resources/lib/deps/Cryptodome/Math/_IntegerBase.py +++ /dev/null @@ -1,412 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2018, Helder Eijs -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import abc - -from Cryptodome.Util.py3compat import iter_range, bord, bchr, ABC - -from Cryptodome import Random - - -class IntegerBase(ABC): - - # Conversions - @abc.abstractmethod - def __int__(self): - pass - - @abc.abstractmethod - def __str__(self): - pass - - @abc.abstractmethod - def __repr__(self): - pass - - @abc.abstractmethod - def to_bytes(self, block_size=0, byteorder='big'): - pass - - @staticmethod - @abc.abstractmethod - def from_bytes(byte_string, byteorder='big'): - pass - - # Relations - @abc.abstractmethod - def __eq__(self, term): - pass - - @abc.abstractmethod - def __ne__(self, term): - pass - - @abc.abstractmethod - def __lt__(self, term): - pass - - @abc.abstractmethod - def __le__(self, term): - pass - - @abc.abstractmethod - def __gt__(self, term): - pass - - @abc.abstractmethod - def __ge__(self, term): - pass - - @abc.abstractmethod - def __nonzero__(self): - pass - __bool__ = __nonzero__ - - @abc.abstractmethod - def is_negative(self): - pass - - # Arithmetic operations - @abc.abstractmethod - def __add__(self, term): - pass - - @abc.abstractmethod - def __sub__(self, term): - pass - - @abc.abstractmethod - def __mul__(self, factor): - pass - - @abc.abstractmethod - def __floordiv__(self, divisor): - pass - - @abc.abstractmethod - def __mod__(self, divisor): - pass - - @abc.abstractmethod - def inplace_pow(self, exponent, modulus=None): - pass - - @abc.abstractmethod - def __pow__(self, exponent, modulus=None): - pass - - @abc.abstractmethod - def __abs__(self): - pass - - @abc.abstractmethod - def sqrt(self, modulus=None): - pass - - @abc.abstractmethod - def __iadd__(self, term): - pass - - @abc.abstractmethod - def __isub__(self, term): - pass - - @abc.abstractmethod - def __imul__(self, term): - pass - - @abc.abstractmethod - def __imod__(self, term): - pass - - # Boolean/bit operations - @abc.abstractmethod - def __and__(self, term): - pass - - @abc.abstractmethod - def __or__(self, term): - pass - - @abc.abstractmethod - def __rshift__(self, pos): - pass - - @abc.abstractmethod - def __irshift__(self, pos): - pass - - @abc.abstractmethod - def __lshift__(self, pos): - pass - - @abc.abstractmethod - def __ilshift__(self, pos): - pass - - @abc.abstractmethod - def get_bit(self, n): - pass - - # Extra - @abc.abstractmethod - def is_odd(self): - pass - - @abc.abstractmethod - def is_even(self): - pass - - @abc.abstractmethod - def size_in_bits(self): - pass - - @abc.abstractmethod - def size_in_bytes(self): - pass - - @abc.abstractmethod - def is_perfect_square(self): - pass - - @abc.abstractmethod - def fail_if_divisible_by(self, small_prime): - pass - - @abc.abstractmethod - def multiply_accumulate(self, a, b): - pass - - @abc.abstractmethod - def set(self, source): - pass - - @abc.abstractmethod - def inplace_inverse(self, modulus): - pass - - @abc.abstractmethod - def inverse(self, modulus): - pass - - @abc.abstractmethod - def gcd(self, term): - pass - - @abc.abstractmethod - def lcm(self, term): - pass - - @staticmethod - @abc.abstractmethod - def jacobi_symbol(a, n): - pass - - @staticmethod - def _tonelli_shanks(n, p): - """Tonelli-shanks algorithm for computing the square root - of n modulo a prime p. - - n must be in the range [0..p-1]. - p must be at least even. - - The return value r is the square root of modulo p. If non-zero, - another solution will also exist (p-r). - - Note we cannot assume that p is really a prime: if it's not, - we can either raise an exception or return the correct value. - """ - - # See https://rosettacode.org/wiki/Tonelli-Shanks_algorithm - - if n in (0, 1): - return n - - if p % 4 == 3: - root = pow(n, (p + 1) // 4, p) - if pow(root, 2, p) != n: - raise ValueError("Cannot compute square root") - return root - - s = 1 - q = (p - 1) // 2 - while not (q & 1): - s += 1 - q >>= 1 - - z = n.__class__(2) - while True: - euler = pow(z, (p - 1) // 2, p) - if euler == 1: - z += 1 - continue - if euler == p - 1: - break - # Most probably p is not a prime - raise ValueError("Cannot compute square root") - - m = s - c = pow(z, q, p) - t = pow(n, q, p) - r = pow(n, (q + 1) // 2, p) - - while t != 1: - for i in iter_range(0, m): - if pow(t, 2**i, p) == 1: - break - if i == m: - raise ValueError("Cannot compute square root of %d mod %d" % (n, p)) - b = pow(c, 2**(m - i - 1), p) - m = i - c = b**2 % p - t = (t * b**2) % p - r = (r * b) % p - - if pow(r, 2, p) != n: - raise ValueError("Cannot compute square root") - - return r - - @classmethod - def random(cls, **kwargs): - """Generate a random natural integer of a certain size. - - :Keywords: - exact_bits : positive integer - The length in bits of the resulting random Integer number. - The number is guaranteed to fulfil the relation: - - 2^bits > result >= 2^(bits - 1) - - max_bits : positive integer - The maximum length in bits of the resulting random Integer number. - The number is guaranteed to fulfil the relation: - - 2^bits > result >=0 - - randfunc : callable - A function that returns a random byte string. The length of the - byte string is passed as parameter. Optional. - If not provided (or ``None``), randomness is read from the system RNG. - - :Return: a Integer object - """ - - exact_bits = kwargs.pop("exact_bits", None) - max_bits = kwargs.pop("max_bits", None) - randfunc = kwargs.pop("randfunc", None) - - if randfunc is None: - randfunc = Random.new().read - - if exact_bits is None and max_bits is None: - raise ValueError("Either 'exact_bits' or 'max_bits' must be specified") - - if exact_bits is not None and max_bits is not None: - raise ValueError("'exact_bits' and 'max_bits' are mutually exclusive") - - bits = exact_bits or max_bits - bytes_needed = ((bits - 1) // 8) + 1 - significant_bits_msb = 8 - (bytes_needed * 8 - bits) - msb = bord(randfunc(1)[0]) - if exact_bits is not None: - msb |= 1 << (significant_bits_msb - 1) - msb &= (1 << significant_bits_msb) - 1 - - return cls.from_bytes(bchr(msb) + randfunc(bytes_needed - 1)) - - @classmethod - def random_range(cls, **kwargs): - """Generate a random integer within a given internal. - - :Keywords: - min_inclusive : integer - The lower end of the interval (inclusive). - max_inclusive : integer - The higher end of the interval (inclusive). - max_exclusive : integer - The higher end of the interval (exclusive). - randfunc : callable - A function that returns a random byte string. The length of the - byte string is passed as parameter. Optional. - If not provided (or ``None``), randomness is read from the system RNG. - :Returns: - An Integer randomly taken in the given interval. - """ - - min_inclusive = kwargs.pop("min_inclusive", None) - max_inclusive = kwargs.pop("max_inclusive", None) - max_exclusive = kwargs.pop("max_exclusive", None) - randfunc = kwargs.pop("randfunc", None) - - if kwargs: - raise ValueError("Unknown keywords: " + str(kwargs.keys)) - if None not in (max_inclusive, max_exclusive): - raise ValueError("max_inclusive and max_exclusive cannot be both" - " specified") - if max_exclusive is not None: - max_inclusive = max_exclusive - 1 - if None in (min_inclusive, max_inclusive): - raise ValueError("Missing keyword to identify the interval") - - if randfunc is None: - randfunc = Random.new().read - - norm_maximum = max_inclusive - min_inclusive - bits_needed = cls(norm_maximum).size_in_bits() - - norm_candidate = -1 - while not 0 <= norm_candidate <= norm_maximum: - norm_candidate = cls.random( - max_bits=bits_needed, - randfunc=randfunc - ) - return norm_candidate + min_inclusive - - @staticmethod - @abc.abstractmethod - def _mult_modulo_bytes(term1, term2, modulus): - """Multiply two integers, take the modulo, and encode as big endian. - This specialized method is used for RSA decryption. - - Args: - term1 : integer - The first term of the multiplication, non-negative. - term2 : integer - The second term of the multiplication, non-negative. - modulus: integer - The modulus, a positive odd number. - :Returns: - A byte string, with the result of the modular multiplication - encoded in big endian mode. - It is as long as the modulus would be, with zero padding - on the left if needed. - """ - pass diff --git a/resources/lib/deps/Cryptodome/Math/_IntegerBase.pyi b/resources/lib/deps/Cryptodome/Math/_IntegerBase.pyi deleted file mode 100644 index ea23532..0000000 --- a/resources/lib/deps/Cryptodome/Math/_IntegerBase.pyi +++ /dev/null @@ -1,67 +0,0 @@ -from typing import Optional, Union, Callable - -RandFunc = Callable[[int],int] - -class IntegerBase: - - def __init__(self, value: Union[IntegerBase, int]): ... - - def __int__(self) -> int: ... - def __str__(self) -> str: ... - def __repr__(self) -> str: ... - def to_bytes(self, block_size: Optional[int]=0, byteorder: str= ...) -> bytes: ... - @staticmethod - def from_bytes(byte_string: bytes, byteorder: Optional[str] = ...) -> IntegerBase: ... - def __eq__(self, term: object) -> bool: ... - def __ne__(self, term: object) -> bool: ... - def __lt__(self, term: Union[IntegerBase, int]) -> bool: ... - def __le__(self, term: Union[IntegerBase, int]) -> bool: ... - def __gt__(self, term: Union[IntegerBase, int]) -> bool: ... - def __ge__(self, term: Union[IntegerBase, int]) -> bool: ... - def __nonzero__(self) -> bool: ... - def is_negative(self) -> bool: ... - def __add__(self, term: Union[IntegerBase, int]) -> IntegerBase: ... - def __sub__(self, term: Union[IntegerBase, int]) -> IntegerBase: ... - def __mul__(self, term: Union[IntegerBase, int]) -> IntegerBase: ... - def __floordiv__(self, divisor: Union[IntegerBase, int]) -> IntegerBase: ... - def __mod__(self, divisor: Union[IntegerBase, int]) -> IntegerBase: ... - def inplace_pow(self, exponent: int, modulus: Optional[Union[IntegerBase, int]]=None) -> IntegerBase: ... - def __pow__(self, exponent: int, modulus: Optional[int]) -> IntegerBase: ... - def __abs__(self) -> IntegerBase: ... - def sqrt(self, modulus: Optional[int]) -> IntegerBase: ... - def __iadd__(self, term: Union[IntegerBase, int]) -> IntegerBase: ... - def __isub__(self, term: Union[IntegerBase, int]) -> IntegerBase: ... - def __imul__(self, term: Union[IntegerBase, int]) -> IntegerBase: ... - def __imod__(self, divisor: Union[IntegerBase, int]) -> IntegerBase: ... - def __and__(self, term: Union[IntegerBase, int]) -> IntegerBase: ... - def __or__(self, term: Union[IntegerBase, int]) -> IntegerBase: ... - def __rshift__(self, pos: Union[IntegerBase, int]) -> IntegerBase: ... - def __irshift__(self, pos: Union[IntegerBase, int]) -> IntegerBase: ... - def __lshift__(self, pos: Union[IntegerBase, int]) -> IntegerBase: ... - def __ilshift__(self, pos: Union[IntegerBase, int]) -> IntegerBase: ... - def get_bit(self, n: int) -> bool: ... - def is_odd(self) -> bool: ... - def is_even(self) -> bool: ... - def size_in_bits(self) -> int: ... - def size_in_bytes(self) -> int: ... - def is_perfect_square(self) -> bool: ... - def fail_if_divisible_by(self, small_prime: Union[IntegerBase, int]) -> None: ... - def multiply_accumulate(self, a: Union[IntegerBase, int], b: Union[IntegerBase, int]) -> IntegerBase: ... - def set(self, source: Union[IntegerBase, int]) -> IntegerBase: ... - def inplace_inverse(self, modulus: Union[IntegerBase, int]) -> IntegerBase: ... - def inverse(self, modulus: Union[IntegerBase, int]) -> IntegerBase: ... - def gcd(self, term: Union[IntegerBase, int]) -> IntegerBase: ... - def lcm(self, term: Union[IntegerBase, int]) -> IntegerBase: ... - @staticmethod - def jacobi_symbol(a: Union[IntegerBase, int], n: Union[IntegerBase, int]) -> IntegerBase: ... - @staticmethod - def _tonelli_shanks(n: Union[IntegerBase, int], p: Union[IntegerBase, int]) -> IntegerBase : ... - @classmethod - def random(cls, **kwargs: Union[int,RandFunc]) -> IntegerBase : ... - @classmethod - def random_range(cls, **kwargs: Union[int,RandFunc]) -> IntegerBase : ... - @staticmethod - def _mult_modulo_bytes(term1: Union[IntegerBase, int], - term2: Union[IntegerBase, int], - modulus: Union[IntegerBase, int]) -> bytes: ... - diff --git a/resources/lib/deps/Cryptodome/Math/_IntegerCustom.py b/resources/lib/deps/Cryptodome/Math/_IntegerCustom.py deleted file mode 100644 index 20eadca..0000000 --- a/resources/lib/deps/Cryptodome/Math/_IntegerCustom.py +++ /dev/null @@ -1,162 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2018, Helder Eijs -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from ._IntegerNative import IntegerNative - -from Cryptodome.Util.number import long_to_bytes, bytes_to_long - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - create_string_buffer, - get_raw_buffer, backend, - c_size_t, c_ulonglong) - - -from Cryptodome.Random.random import getrandbits - -c_defs = """ -int monty_pow(uint8_t *out, - const uint8_t *base, - const uint8_t *exp, - const uint8_t *modulus, - size_t len, - uint64_t seed); - -int monty_multiply(uint8_t *out, - const uint8_t *term1, - const uint8_t *term2, - const uint8_t *modulus, - size_t len); -""" - - -_raw_montgomery = load_pycryptodome_raw_lib("Cryptodome.Math._modexp", c_defs) -implementation = {"library": "custom", "api": backend} - - -class IntegerCustom(IntegerNative): - - @staticmethod - def from_bytes(byte_string, byteorder='big'): - if byteorder == 'big': - pass - elif byteorder == 'little': - byte_string = bytearray(byte_string) - byte_string.reverse() - else: - raise ValueError("Incorrect byteorder") - return IntegerCustom(bytes_to_long(byte_string)) - - def inplace_pow(self, exponent, modulus=None): - exp_value = int(exponent) - if exp_value < 0: - raise ValueError("Exponent must not be negative") - - # No modular reduction - if modulus is None: - self._value = pow(self._value, exp_value) - return self - - # With modular reduction - mod_value = int(modulus) - if mod_value < 0: - raise ValueError("Modulus must be positive") - if mod_value == 0: - raise ZeroDivisionError("Modulus cannot be zero") - - # C extension only works with odd moduli - if (mod_value & 1) == 0: - self._value = pow(self._value, exp_value, mod_value) - return self - - # C extension only works with bases smaller than modulus - if self._value >= mod_value: - self._value %= mod_value - - max_len = len(long_to_bytes(max(self._value, exp_value, mod_value))) - - base_b = long_to_bytes(self._value, max_len) - exp_b = long_to_bytes(exp_value, max_len) - modulus_b = long_to_bytes(mod_value, max_len) - - out = create_string_buffer(max_len) - - error = _raw_montgomery.monty_pow( - out, - base_b, - exp_b, - modulus_b, - c_size_t(max_len), - c_ulonglong(getrandbits(64)) - ) - - if error: - raise ValueError("monty_pow failed with error: %d" % error) - - result = bytes_to_long(get_raw_buffer(out)) - self._value = result - return self - - @staticmethod - def _mult_modulo_bytes(term1, term2, modulus): - - # With modular reduction - mod_value = int(modulus) - if mod_value < 0: - raise ValueError("Modulus must be positive") - if mod_value == 0: - raise ZeroDivisionError("Modulus cannot be zero") - - # C extension only works with odd moduli - if (mod_value & 1) == 0: - raise ValueError("Odd modulus is required") - - # C extension only works with non-negative terms smaller than modulus - if term1 >= mod_value or term1 < 0: - term1 %= mod_value - if term2 >= mod_value or term2 < 0: - term2 %= mod_value - - modulus_b = long_to_bytes(mod_value) - numbers_len = len(modulus_b) - term1_b = long_to_bytes(term1, numbers_len) - term2_b = long_to_bytes(term2, numbers_len) - out = create_string_buffer(numbers_len) - - error = _raw_montgomery.monty_multiply( - out, - term1_b, - term2_b, - modulus_b, - c_size_t(numbers_len) - ) - if error: - raise ValueError("monty_multiply failed with error: %d" % error) - - return get_raw_buffer(out) diff --git a/resources/lib/deps/Cryptodome/Math/_IntegerCustom.pyi b/resources/lib/deps/Cryptodome/Math/_IntegerCustom.pyi deleted file mode 100644 index 2dd75c7..0000000 --- a/resources/lib/deps/Cryptodome/Math/_IntegerCustom.pyi +++ /dev/null @@ -1,8 +0,0 @@ -from typing import Any - -from ._IntegerNative import IntegerNative - -_raw_montgomery = Any - -class IntegerCustom(IntegerNative): - pass diff --git a/resources/lib/deps/Cryptodome/Math/_IntegerGMP.py b/resources/lib/deps/Cryptodome/Math/_IntegerGMP.py deleted file mode 100644 index f58f044..0000000 --- a/resources/lib/deps/Cryptodome/Math/_IntegerGMP.py +++ /dev/null @@ -1,782 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import sys - -from Cryptodome.Util.py3compat import tobytes, is_native_int - -from Cryptodome.Util._raw_api import (backend, load_lib, - get_raw_buffer, get_c_string, - null_pointer, create_string_buffer, - c_ulong, c_size_t, c_uint8_ptr) - -from ._IntegerBase import IntegerBase - -gmp_defs = """typedef unsigned long UNIX_ULONG; - typedef struct { int a; int b; void *c; } MPZ; - typedef MPZ mpz_t[1]; - typedef UNIX_ULONG mp_bitcnt_t; - - void __gmpz_init (mpz_t x); - void __gmpz_init_set (mpz_t rop, const mpz_t op); - void __gmpz_init_set_ui (mpz_t rop, UNIX_ULONG op); - - UNIX_ULONG __gmpz_get_ui (const mpz_t op); - void __gmpz_set (mpz_t rop, const mpz_t op); - void __gmpz_set_ui (mpz_t rop, UNIX_ULONG op); - void __gmpz_add (mpz_t rop, const mpz_t op1, const mpz_t op2); - void __gmpz_add_ui (mpz_t rop, const mpz_t op1, UNIX_ULONG op2); - void __gmpz_sub_ui (mpz_t rop, const mpz_t op1, UNIX_ULONG op2); - void __gmpz_addmul (mpz_t rop, const mpz_t op1, const mpz_t op2); - void __gmpz_addmul_ui (mpz_t rop, const mpz_t op1, UNIX_ULONG op2); - void __gmpz_submul_ui (mpz_t rop, const mpz_t op1, UNIX_ULONG op2); - void __gmpz_import (mpz_t rop, size_t count, int order, size_t size, - int endian, size_t nails, const void *op); - void * __gmpz_export (void *rop, size_t *countp, int order, - size_t size, - int endian, size_t nails, const mpz_t op); - size_t __gmpz_sizeinbase (const mpz_t op, int base); - void __gmpz_sub (mpz_t rop, const mpz_t op1, const mpz_t op2); - void __gmpz_mul (mpz_t rop, const mpz_t op1, const mpz_t op2); - void __gmpz_mul_ui (mpz_t rop, const mpz_t op1, UNIX_ULONG op2); - int __gmpz_cmp (const mpz_t op1, const mpz_t op2); - void __gmpz_powm (mpz_t rop, const mpz_t base, const mpz_t exp, const - mpz_t mod); - void __gmpz_powm_ui (mpz_t rop, const mpz_t base, UNIX_ULONG exp, - const mpz_t mod); - void __gmpz_pow_ui (mpz_t rop, const mpz_t base, UNIX_ULONG exp); - void __gmpz_sqrt(mpz_t rop, const mpz_t op); - void __gmpz_mod (mpz_t r, const mpz_t n, const mpz_t d); - void __gmpz_neg (mpz_t rop, const mpz_t op); - void __gmpz_abs (mpz_t rop, const mpz_t op); - void __gmpz_and (mpz_t rop, const mpz_t op1, const mpz_t op2); - void __gmpz_ior (mpz_t rop, const mpz_t op1, const mpz_t op2); - void __gmpz_clear (mpz_t x); - void __gmpz_tdiv_q_2exp (mpz_t q, const mpz_t n, mp_bitcnt_t b); - void __gmpz_fdiv_q (mpz_t q, const mpz_t n, const mpz_t d); - void __gmpz_mul_2exp (mpz_t rop, const mpz_t op1, mp_bitcnt_t op2); - int __gmpz_tstbit (const mpz_t op, mp_bitcnt_t bit_index); - int __gmpz_perfect_square_p (const mpz_t op); - int __gmpz_jacobi (const mpz_t a, const mpz_t b); - void __gmpz_gcd (mpz_t rop, const mpz_t op1, const mpz_t op2); - UNIX_ULONG __gmpz_gcd_ui (mpz_t rop, const mpz_t op1, - UNIX_ULONG op2); - void __gmpz_lcm (mpz_t rop, const mpz_t op1, const mpz_t op2); - int __gmpz_invert (mpz_t rop, const mpz_t op1, const mpz_t op2); - int __gmpz_divisible_p (const mpz_t n, const mpz_t d); - int __gmpz_divisible_ui_p (const mpz_t n, UNIX_ULONG d); - """ - -if sys.platform == "win32": - raise ImportError("Not using GMP on Windows") - -lib = load_lib("gmp", gmp_defs) -implementation = {"library": "gmp", "api": backend} - -if hasattr(lib, "__mpir_version"): - raise ImportError("MPIR library detected") - -# In order to create a function that returns a pointer to -# a new MPZ structure, we need to break the abstraction -# and know exactly what ffi backend we have -if implementation["api"] == "ctypes": - from ctypes import Structure, c_int, c_void_p, byref - - class _MPZ(Structure): - _fields_ = [('_mp_alloc', c_int), - ('_mp_size', c_int), - ('_mp_d', c_void_p)] - - def new_mpz(): - return byref(_MPZ()) - -else: - # We are using CFFI - from Cryptodome.Util._raw_api import ffi - - def new_mpz(): - return ffi.new("MPZ*") - - -# Lazy creation of GMP methods -class _GMP(object): - - def __getattr__(self, name): - if name.startswith("mpz_"): - func_name = "__gmpz_" + name[4:] - elif name.startswith("gmp_"): - func_name = "__gmp_" + name[4:] - else: - raise AttributeError("Attribute %s is invalid" % name) - func = getattr(lib, func_name) - setattr(self, name, func) - return func - - -_gmp = _GMP() - - -class IntegerGMP(IntegerBase): - """A fast, arbitrary precision integer""" - - _zero_mpz_p = new_mpz() - _gmp.mpz_init_set_ui(_zero_mpz_p, c_ulong(0)) - - def __init__(self, value): - """Initialize the integer to the given value.""" - - self._mpz_p = new_mpz() - self._initialized = False - - if isinstance(value, float): - raise ValueError("A floating point type is not a natural number") - - if is_native_int(value): - _gmp.mpz_init(self._mpz_p) - self._initialized = True - if value == 0: - return - - tmp = new_mpz() - _gmp.mpz_init(tmp) - - try: - positive = value >= 0 - reduce = abs(value) - slots = (reduce.bit_length() - 1) // 32 + 1 - - while slots > 0: - slots = slots - 1 - _gmp.mpz_set_ui(tmp, - c_ulong(0xFFFFFFFF & (reduce >> (slots * 32)))) - _gmp.mpz_mul_2exp(tmp, tmp, c_ulong(slots * 32)) - _gmp.mpz_add(self._mpz_p, self._mpz_p, tmp) - finally: - _gmp.mpz_clear(tmp) - - if not positive: - _gmp.mpz_neg(self._mpz_p, self._mpz_p) - - elif isinstance(value, IntegerGMP): - _gmp.mpz_init_set(self._mpz_p, value._mpz_p) - self._initialized = True - else: - raise NotImplementedError - - - # Conversions - def __int__(self): - tmp = new_mpz() - _gmp.mpz_init_set(tmp, self._mpz_p) - - try: - value = 0 - slot = 0 - while _gmp.mpz_cmp(tmp, self._zero_mpz_p) != 0: - lsb = _gmp.mpz_get_ui(tmp) & 0xFFFFFFFF - value |= lsb << (slot * 32) - _gmp.mpz_tdiv_q_2exp(tmp, tmp, c_ulong(32)) - slot = slot + 1 - finally: - _gmp.mpz_clear(tmp) - - if self < 0: - value = -value - return int(value) - - def __str__(self): - return str(int(self)) - - def __repr__(self): - return "Integer(%s)" % str(self) - - # Only Python 2.x - def __hex__(self): - return hex(int(self)) - - # Only Python 3.x - def __index__(self): - return int(self) - - def to_bytes(self, block_size=0, byteorder='big'): - """Convert the number into a byte string. - - This method encodes the number in network order and prepends - as many zero bytes as required. It only works for non-negative - values. - - :Parameters: - block_size : integer - The exact size the output byte string must have. - If zero, the string has the minimal length. - byteorder : string - 'big' for big-endian integers (default), 'little' for litte-endian. - :Returns: - A byte string. - :Raise ValueError: - If the value is negative or if ``block_size`` is - provided and the length of the byte string would exceed it. - """ - - if self < 0: - raise ValueError("Conversion only valid for non-negative numbers") - - buf_len = (_gmp.mpz_sizeinbase(self._mpz_p, 2) + 7) // 8 - if buf_len > block_size > 0: - raise ValueError("Number is too big to convert to byte string" - " of prescribed length") - buf = create_string_buffer(buf_len) - - - _gmp.mpz_export( - buf, - null_pointer, # Ignore countp - 1, # Big endian - c_size_t(1), # Each word is 1 byte long - 0, # Endianess within a word - not relevant - c_size_t(0), # No nails - self._mpz_p) - - result = b'\x00' * max(0, block_size - buf_len) + get_raw_buffer(buf) - if byteorder == 'big': - pass - elif byteorder == 'little': - result = bytearray(result) - result.reverse() - result = bytes(result) - else: - raise ValueError("Incorrect byteorder") - return result - - @staticmethod - def from_bytes(byte_string, byteorder='big'): - """Convert a byte string into a number. - - :Parameters: - byte_string : byte string - The input number, encoded in network order. - It can only be non-negative. - byteorder : string - 'big' for big-endian integers (default), 'little' for litte-endian. - - :Return: - The ``Integer`` object carrying the same value as the input. - """ - result = IntegerGMP(0) - if byteorder == 'big': - pass - elif byteorder == 'little': - byte_string = bytearray(byte_string) - byte_string.reverse() - else: - raise ValueError("Incorrect byteorder") - _gmp.mpz_import( - result._mpz_p, - c_size_t(len(byte_string)), # Amount of words to read - 1, # Big endian - c_size_t(1), # Each word is 1 byte long - 0, # Endianess within a word - not relevant - c_size_t(0), # No nails - c_uint8_ptr(byte_string)) - return result - - # Relations - def _apply_and_return(self, func, term): - if not isinstance(term, IntegerGMP): - term = IntegerGMP(term) - return func(self._mpz_p, term._mpz_p) - - def __eq__(self, term): - if not (isinstance(term, IntegerGMP) or is_native_int(term)): - return False - return self._apply_and_return(_gmp.mpz_cmp, term) == 0 - - def __ne__(self, term): - if not (isinstance(term, IntegerGMP) or is_native_int(term)): - return True - return self._apply_and_return(_gmp.mpz_cmp, term) != 0 - - def __lt__(self, term): - return self._apply_and_return(_gmp.mpz_cmp, term) < 0 - - def __le__(self, term): - return self._apply_and_return(_gmp.mpz_cmp, term) <= 0 - - def __gt__(self, term): - return self._apply_and_return(_gmp.mpz_cmp, term) > 0 - - def __ge__(self, term): - return self._apply_and_return(_gmp.mpz_cmp, term) >= 0 - - def __nonzero__(self): - return _gmp.mpz_cmp(self._mpz_p, self._zero_mpz_p) != 0 - __bool__ = __nonzero__ - - def is_negative(self): - return _gmp.mpz_cmp(self._mpz_p, self._zero_mpz_p) < 0 - - # Arithmetic operations - def __add__(self, term): - result = IntegerGMP(0) - if not isinstance(term, IntegerGMP): - try: - term = IntegerGMP(term) - except NotImplementedError: - return NotImplemented - _gmp.mpz_add(result._mpz_p, - self._mpz_p, - term._mpz_p) - return result - - def __sub__(self, term): - result = IntegerGMP(0) - if not isinstance(term, IntegerGMP): - try: - term = IntegerGMP(term) - except NotImplementedError: - return NotImplemented - _gmp.mpz_sub(result._mpz_p, - self._mpz_p, - term._mpz_p) - return result - - def __mul__(self, term): - result = IntegerGMP(0) - if not isinstance(term, IntegerGMP): - try: - term = IntegerGMP(term) - except NotImplementedError: - return NotImplemented - _gmp.mpz_mul(result._mpz_p, - self._mpz_p, - term._mpz_p) - return result - - def __floordiv__(self, divisor): - if not isinstance(divisor, IntegerGMP): - divisor = IntegerGMP(divisor) - if _gmp.mpz_cmp(divisor._mpz_p, - self._zero_mpz_p) == 0: - raise ZeroDivisionError("Division by zero") - result = IntegerGMP(0) - _gmp.mpz_fdiv_q(result._mpz_p, - self._mpz_p, - divisor._mpz_p) - return result - - def __mod__(self, divisor): - if not isinstance(divisor, IntegerGMP): - divisor = IntegerGMP(divisor) - comp = _gmp.mpz_cmp(divisor._mpz_p, - self._zero_mpz_p) - if comp == 0: - raise ZeroDivisionError("Division by zero") - if comp < 0: - raise ValueError("Modulus must be positive") - result = IntegerGMP(0) - _gmp.mpz_mod(result._mpz_p, - self._mpz_p, - divisor._mpz_p) - return result - - def inplace_pow(self, exponent, modulus=None): - - if modulus is None: - if exponent < 0: - raise ValueError("Exponent must not be negative") - - # Normal exponentiation - if exponent > 256: - raise ValueError("Exponent is too big") - _gmp.mpz_pow_ui(self._mpz_p, - self._mpz_p, # Base - c_ulong(int(exponent)) - ) - else: - # Modular exponentiation - if not isinstance(modulus, IntegerGMP): - modulus = IntegerGMP(modulus) - if not modulus: - raise ZeroDivisionError("Division by zero") - if modulus.is_negative(): - raise ValueError("Modulus must be positive") - if is_native_int(exponent): - if exponent < 0: - raise ValueError("Exponent must not be negative") - if exponent < 65536: - _gmp.mpz_powm_ui(self._mpz_p, - self._mpz_p, - c_ulong(exponent), - modulus._mpz_p) - return self - exponent = IntegerGMP(exponent) - elif exponent.is_negative(): - raise ValueError("Exponent must not be negative") - _gmp.mpz_powm(self._mpz_p, - self._mpz_p, - exponent._mpz_p, - modulus._mpz_p) - return self - - def __pow__(self, exponent, modulus=None): - result = IntegerGMP(self) - return result.inplace_pow(exponent, modulus) - - def __abs__(self): - result = IntegerGMP(0) - _gmp.mpz_abs(result._mpz_p, self._mpz_p) - return result - - def sqrt(self, modulus=None): - """Return the largest Integer that does not - exceed the square root""" - - if modulus is None: - if self < 0: - raise ValueError("Square root of negative value") - result = IntegerGMP(0) - _gmp.mpz_sqrt(result._mpz_p, - self._mpz_p) - else: - if modulus <= 0: - raise ValueError("Modulus must be positive") - modulus = int(modulus) - result = IntegerGMP(self._tonelli_shanks(int(self) % modulus, modulus)) - - return result - - def __iadd__(self, term): - if is_native_int(term): - if 0 <= term < 65536: - _gmp.mpz_add_ui(self._mpz_p, - self._mpz_p, - c_ulong(term)) - return self - if -65535 < term < 0: - _gmp.mpz_sub_ui(self._mpz_p, - self._mpz_p, - c_ulong(-term)) - return self - term = IntegerGMP(term) - _gmp.mpz_add(self._mpz_p, - self._mpz_p, - term._mpz_p) - return self - - def __isub__(self, term): - if is_native_int(term): - if 0 <= term < 65536: - _gmp.mpz_sub_ui(self._mpz_p, - self._mpz_p, - c_ulong(term)) - return self - if -65535 < term < 0: - _gmp.mpz_add_ui(self._mpz_p, - self._mpz_p, - c_ulong(-term)) - return self - term = IntegerGMP(term) - _gmp.mpz_sub(self._mpz_p, - self._mpz_p, - term._mpz_p) - return self - - def __imul__(self, term): - if is_native_int(term): - if 0 <= term < 65536: - _gmp.mpz_mul_ui(self._mpz_p, - self._mpz_p, - c_ulong(term)) - return self - if -65535 < term < 0: - _gmp.mpz_mul_ui(self._mpz_p, - self._mpz_p, - c_ulong(-term)) - _gmp.mpz_neg(self._mpz_p, self._mpz_p) - return self - term = IntegerGMP(term) - _gmp.mpz_mul(self._mpz_p, - self._mpz_p, - term._mpz_p) - return self - - def __imod__(self, divisor): - if not isinstance(divisor, IntegerGMP): - divisor = IntegerGMP(divisor) - comp = _gmp.mpz_cmp(divisor._mpz_p, - divisor._zero_mpz_p) - if comp == 0: - raise ZeroDivisionError("Division by zero") - if comp < 0: - raise ValueError("Modulus must be positive") - _gmp.mpz_mod(self._mpz_p, - self._mpz_p, - divisor._mpz_p) - return self - - # Boolean/bit operations - def __and__(self, term): - result = IntegerGMP(0) - if not isinstance(term, IntegerGMP): - term = IntegerGMP(term) - _gmp.mpz_and(result._mpz_p, - self._mpz_p, - term._mpz_p) - return result - - def __or__(self, term): - result = IntegerGMP(0) - if not isinstance(term, IntegerGMP): - term = IntegerGMP(term) - _gmp.mpz_ior(result._mpz_p, - self._mpz_p, - term._mpz_p) - return result - - def __rshift__(self, pos): - result = IntegerGMP(0) - if pos < 0: - raise ValueError("negative shift count") - if pos > 65536: - if self < 0: - return -1 - else: - return 0 - _gmp.mpz_tdiv_q_2exp(result._mpz_p, - self._mpz_p, - c_ulong(int(pos))) - return result - - def __irshift__(self, pos): - if pos < 0: - raise ValueError("negative shift count") - if pos > 65536: - if self < 0: - return -1 - else: - return 0 - _gmp.mpz_tdiv_q_2exp(self._mpz_p, - self._mpz_p, - c_ulong(int(pos))) - return self - - def __lshift__(self, pos): - result = IntegerGMP(0) - if not 0 <= pos < 65536: - raise ValueError("Incorrect shift count") - _gmp.mpz_mul_2exp(result._mpz_p, - self._mpz_p, - c_ulong(int(pos))) - return result - - def __ilshift__(self, pos): - if not 0 <= pos < 65536: - raise ValueError("Incorrect shift count") - _gmp.mpz_mul_2exp(self._mpz_p, - self._mpz_p, - c_ulong(int(pos))) - return self - - def get_bit(self, n): - """Return True if the n-th bit is set to 1. - Bit 0 is the least significant.""" - - if self < 0: - raise ValueError("no bit representation for negative values") - if n < 0: - raise ValueError("negative bit count") - if n > 65536: - return 0 - return bool(_gmp.mpz_tstbit(self._mpz_p, - c_ulong(int(n)))) - - # Extra - def is_odd(self): - return _gmp.mpz_tstbit(self._mpz_p, 0) == 1 - - def is_even(self): - return _gmp.mpz_tstbit(self._mpz_p, 0) == 0 - - def size_in_bits(self): - """Return the minimum number of bits that can encode the number.""" - - if self < 0: - raise ValueError("Conversion only valid for non-negative numbers") - return _gmp.mpz_sizeinbase(self._mpz_p, 2) - - def size_in_bytes(self): - """Return the minimum number of bytes that can encode the number.""" - return (self.size_in_bits() - 1) // 8 + 1 - - def is_perfect_square(self): - return _gmp.mpz_perfect_square_p(self._mpz_p) != 0 - - def fail_if_divisible_by(self, small_prime): - """Raise an exception if the small prime is a divisor.""" - - if is_native_int(small_prime): - if 0 < small_prime < 65536: - if _gmp.mpz_divisible_ui_p(self._mpz_p, - c_ulong(small_prime)): - raise ValueError("The value is composite") - return - small_prime = IntegerGMP(small_prime) - if _gmp.mpz_divisible_p(self._mpz_p, - small_prime._mpz_p): - raise ValueError("The value is composite") - - def multiply_accumulate(self, a, b): - """Increment the number by the product of a and b.""" - - if not isinstance(a, IntegerGMP): - a = IntegerGMP(a) - if is_native_int(b): - if 0 < b < 65536: - _gmp.mpz_addmul_ui(self._mpz_p, - a._mpz_p, - c_ulong(b)) - return self - if -65535 < b < 0: - _gmp.mpz_submul_ui(self._mpz_p, - a._mpz_p, - c_ulong(-b)) - return self - b = IntegerGMP(b) - _gmp.mpz_addmul(self._mpz_p, - a._mpz_p, - b._mpz_p) - return self - - def set(self, source): - """Set the Integer to have the given value""" - - if not isinstance(source, IntegerGMP): - source = IntegerGMP(source) - _gmp.mpz_set(self._mpz_p, - source._mpz_p) - return self - - def inplace_inverse(self, modulus): - """Compute the inverse of this number in the ring of - modulo integers. - - Raise an exception if no inverse exists. - """ - - if not isinstance(modulus, IntegerGMP): - modulus = IntegerGMP(modulus) - - comp = _gmp.mpz_cmp(modulus._mpz_p, - self._zero_mpz_p) - if comp == 0: - raise ZeroDivisionError("Modulus cannot be zero") - if comp < 0: - raise ValueError("Modulus must be positive") - - result = _gmp.mpz_invert(self._mpz_p, - self._mpz_p, - modulus._mpz_p) - if not result: - raise ValueError("No inverse value can be computed") - return self - - def inverse(self, modulus): - result = IntegerGMP(self) - result.inplace_inverse(modulus) - return result - - def gcd(self, term): - """Compute the greatest common denominator between this - number and another term.""" - - result = IntegerGMP(0) - if is_native_int(term): - if 0 < term < 65535: - _gmp.mpz_gcd_ui(result._mpz_p, - self._mpz_p, - c_ulong(term)) - return result - term = IntegerGMP(term) - _gmp.mpz_gcd(result._mpz_p, self._mpz_p, term._mpz_p) - return result - - def lcm(self, term): - """Compute the least common multiplier between this - number and another term.""" - - result = IntegerGMP(0) - if not isinstance(term, IntegerGMP): - term = IntegerGMP(term) - _gmp.mpz_lcm(result._mpz_p, self._mpz_p, term._mpz_p) - return result - - @staticmethod - def jacobi_symbol(a, n): - """Compute the Jacobi symbol""" - - if not isinstance(a, IntegerGMP): - a = IntegerGMP(a) - if not isinstance(n, IntegerGMP): - n = IntegerGMP(n) - if n <= 0 or n.is_even(): - raise ValueError("n must be positive odd for the Jacobi symbol") - return _gmp.mpz_jacobi(a._mpz_p, n._mpz_p) - - @staticmethod - def _mult_modulo_bytes(term1, term2, modulus): - if not isinstance(term1, IntegerGMP): - term1 = IntegerGMP(term1) - if not isinstance(term2, IntegerGMP): - term2 = IntegerGMP(term2) - if not isinstance(modulus, IntegerGMP): - modulus = IntegerGMP(modulus) - - if modulus < 0: - raise ValueError("Modulus must be positive") - if modulus == 0: - raise ZeroDivisionError("Modulus cannot be zero") - if (modulus & 1) == 0: - raise ValueError("Odd modulus is required") - - numbers_len = len(modulus.to_bytes()) - result = ((term1 * term2) % modulus).to_bytes(numbers_len) - return result - - # Clean-up - def __del__(self): - - try: - if self._mpz_p is not None: - if self._initialized: - _gmp.mpz_clear(self._mpz_p) - - self._mpz_p = None - except AttributeError: - pass diff --git a/resources/lib/deps/Cryptodome/Math/_IntegerGMP.pyi b/resources/lib/deps/Cryptodome/Math/_IntegerGMP.pyi deleted file mode 100644 index 2181b47..0000000 --- a/resources/lib/deps/Cryptodome/Math/_IntegerGMP.pyi +++ /dev/null @@ -1,3 +0,0 @@ -from ._IntegerBase import IntegerBase -class IntegerGMP(IntegerBase): - pass diff --git a/resources/lib/deps/Cryptodome/Math/_IntegerNative.py b/resources/lib/deps/Cryptodome/Math/_IntegerNative.py deleted file mode 100644 index 5f768e2..0000000 --- a/resources/lib/deps/Cryptodome/Math/_IntegerNative.py +++ /dev/null @@ -1,382 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from ._IntegerBase import IntegerBase - -from Cryptodome.Util.number import long_to_bytes, bytes_to_long, inverse, GCD - - -class IntegerNative(IntegerBase): - """A class to model a natural integer (including zero)""" - - def __init__(self, value): - if isinstance(value, float): - raise ValueError("A floating point type is not a natural number") - try: - self._value = value._value - except AttributeError: - self._value = value - - # Conversions - def __int__(self): - return self._value - - def __str__(self): - return str(int(self)) - - def __repr__(self): - return "Integer(%s)" % str(self) - - # Only Python 2.x - def __hex__(self): - return hex(self._value) - - # Only Python 3.x - def __index__(self): - return int(self._value) - - def to_bytes(self, block_size=0, byteorder='big'): - if self._value < 0: - raise ValueError("Conversion only valid for non-negative numbers") - result = long_to_bytes(self._value, block_size) - if len(result) > block_size > 0: - raise ValueError("Value too large to encode") - if byteorder == 'big': - pass - elif byteorder == 'little': - result = bytearray(result) - result.reverse() - result = bytes(result) - else: - raise ValueError("Incorrect byteorder") - return result - - @classmethod - def from_bytes(cls, byte_string, byteorder='big'): - if byteorder == 'big': - pass - elif byteorder == 'little': - byte_string = bytearray(byte_string) - byte_string.reverse() - else: - raise ValueError("Incorrect byteorder") - return cls(bytes_to_long(byte_string)) - - # Relations - def __eq__(self, term): - if term is None: - return False - return self._value == int(term) - - def __ne__(self, term): - return not self.__eq__(term) - - def __lt__(self, term): - return self._value < int(term) - - def __le__(self, term): - return self.__lt__(term) or self.__eq__(term) - - def __gt__(self, term): - return not self.__le__(term) - - def __ge__(self, term): - return not self.__lt__(term) - - def __nonzero__(self): - return self._value != 0 - __bool__ = __nonzero__ - - def is_negative(self): - return self._value < 0 - - # Arithmetic operations - def __add__(self, term): - try: - return self.__class__(self._value + int(term)) - except (ValueError, AttributeError, TypeError): - return NotImplemented - - def __sub__(self, term): - try: - return self.__class__(self._value - int(term)) - except (ValueError, AttributeError, TypeError): - return NotImplemented - - def __mul__(self, factor): - try: - return self.__class__(self._value * int(factor)) - except (ValueError, AttributeError, TypeError): - return NotImplemented - - def __floordiv__(self, divisor): - return self.__class__(self._value // int(divisor)) - - def __mod__(self, divisor): - divisor_value = int(divisor) - if divisor_value < 0: - raise ValueError("Modulus must be positive") - return self.__class__(self._value % divisor_value) - - def inplace_pow(self, exponent, modulus=None): - exp_value = int(exponent) - if exp_value < 0: - raise ValueError("Exponent must not be negative") - - if modulus is not None: - mod_value = int(modulus) - if mod_value < 0: - raise ValueError("Modulus must be positive") - if mod_value == 0: - raise ZeroDivisionError("Modulus cannot be zero") - else: - mod_value = None - self._value = pow(self._value, exp_value, mod_value) - return self - - def __pow__(self, exponent, modulus=None): - result = self.__class__(self) - return result.inplace_pow(exponent, modulus) - - def __abs__(self): - return abs(self._value) - - def sqrt(self, modulus=None): - - value = self._value - if modulus is None: - if value < 0: - raise ValueError("Square root of negative value") - # http://stackoverflow.com/questions/15390807/integer-square-root-in-python - - x = value - y = (x + 1) // 2 - while y < x: - x = y - y = (x + value // x) // 2 - result = x - else: - if modulus <= 0: - raise ValueError("Modulus must be positive") - result = self._tonelli_shanks(self % modulus, modulus) - - return self.__class__(result) - - def __iadd__(self, term): - self._value += int(term) - return self - - def __isub__(self, term): - self._value -= int(term) - return self - - def __imul__(self, term): - self._value *= int(term) - return self - - def __imod__(self, term): - modulus = int(term) - if modulus == 0: - raise ZeroDivisionError("Division by zero") - if modulus < 0: - raise ValueError("Modulus must be positive") - self._value %= modulus - return self - - # Boolean/bit operations - def __and__(self, term): - return self.__class__(self._value & int(term)) - - def __or__(self, term): - return self.__class__(self._value | int(term)) - - def __rshift__(self, pos): - try: - return self.__class__(self._value >> int(pos)) - except OverflowError: - if self._value >= 0: - return 0 - else: - return -1 - - def __irshift__(self, pos): - try: - self._value >>= int(pos) - except OverflowError: - if self._value >= 0: - return 0 - else: - return -1 - return self - - def __lshift__(self, pos): - try: - return self.__class__(self._value << int(pos)) - except OverflowError: - raise ValueError("Incorrect shift count") - - def __ilshift__(self, pos): - try: - self._value <<= int(pos) - except OverflowError: - raise ValueError("Incorrect shift count") - return self - - def get_bit(self, n): - if self._value < 0: - raise ValueError("no bit representation for negative values") - try: - try: - result = (self._value >> n._value) & 1 - if n._value < 0: - raise ValueError("negative bit count") - except AttributeError: - result = (self._value >> n) & 1 - if n < 0: - raise ValueError("negative bit count") - except OverflowError: - result = 0 - return result - - # Extra - def is_odd(self): - return (self._value & 1) == 1 - - def is_even(self): - return (self._value & 1) == 0 - - def size_in_bits(self): - - if self._value < 0: - raise ValueError("Conversion only valid for non-negative numbers") - - if self._value == 0: - return 1 - - return self._value.bit_length() - - def size_in_bytes(self): - return (self.size_in_bits() - 1) // 8 + 1 - - def is_perfect_square(self): - if self._value < 0: - return False - if self._value in (0, 1): - return True - - x = self._value // 2 - square_x = x ** 2 - - while square_x > self._value: - x = (square_x + self._value) // (2 * x) - square_x = x ** 2 - - return self._value == x ** 2 - - def fail_if_divisible_by(self, small_prime): - if (self._value % int(small_prime)) == 0: - raise ValueError("Value is composite") - - def multiply_accumulate(self, a, b): - self._value += int(a) * int(b) - return self - - def set(self, source): - self._value = int(source) - - def inplace_inverse(self, modulus): - self._value = inverse(self._value, int(modulus)) - return self - - def inverse(self, modulus): - result = self.__class__(self) - result.inplace_inverse(modulus) - return result - - def gcd(self, term): - return self.__class__(GCD(abs(self._value), abs(int(term)))) - - def lcm(self, term): - term = int(term) - if self._value == 0 or term == 0: - return self.__class__(0) - return self.__class__(abs((self._value * term) // self.gcd(term)._value)) - - @staticmethod - def jacobi_symbol(a, n): - a = int(a) - n = int(n) - - if n <= 0: - raise ValueError("n must be a positive integer") - - if (n & 1) == 0: - raise ValueError("n must be odd for the Jacobi symbol") - - # Step 1 - a = a % n - # Step 2 - if a == 1 or n == 1: - return 1 - # Step 3 - if a == 0: - return 0 - # Step 4 - e = 0 - a1 = a - while (a1 & 1) == 0: - a1 >>= 1 - e += 1 - # Step 5 - if (e & 1) == 0: - s = 1 - elif n % 8 in (1, 7): - s = 1 - else: - s = -1 - # Step 6 - if n % 4 == 3 and a1 % 4 == 3: - s = -s - # Step 7 - n1 = n % a1 - # Step 8 - return s * IntegerNative.jacobi_symbol(n1, a1) - - @staticmethod - def _mult_modulo_bytes(term1, term2, modulus): - if modulus < 0: - raise ValueError("Modulus must be positive") - if modulus == 0: - raise ZeroDivisionError("Modulus cannot be zero") - if (modulus & 1) == 0: - raise ValueError("Odd modulus is required") - - number_len = len(long_to_bytes(modulus)) - return long_to_bytes((term1 * term2) % modulus, number_len) diff --git a/resources/lib/deps/Cryptodome/Math/_IntegerNative.pyi b/resources/lib/deps/Cryptodome/Math/_IntegerNative.pyi deleted file mode 100644 index 3f65a39..0000000 --- a/resources/lib/deps/Cryptodome/Math/_IntegerNative.pyi +++ /dev/null @@ -1,3 +0,0 @@ -from ._IntegerBase import IntegerBase -class IntegerNative(IntegerBase): - pass diff --git a/resources/lib/deps/Cryptodome/Math/__init__.py b/resources/lib/deps/Cryptodome/Math/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/resources/lib/deps/Cryptodome/Math/_modexp.abi3.so b/resources/lib/deps/Cryptodome/Math/_modexp.abi3.so deleted file mode 100755 index d11de72ab59a48f622ba75ae7c3012e2651b1fb2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 213552 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 EU)V8fHvj+t diff --git a/resources/lib/deps/Cryptodome/Protocol/DH.py b/resources/lib/deps/Cryptodome/Protocol/DH.py deleted file mode 100644 index bb174f0..0000000 --- a/resources/lib/deps/Cryptodome/Protocol/DH.py +++ /dev/null @@ -1,101 +0,0 @@ -from Cryptodome.Util.number import long_to_bytes -from Cryptodome.PublicKey.ECC import EccKey - - -def _compute_ecdh(key_priv, key_pub): - # See Section 5.7.1.2 in NIST SP 800-56Ar3 - pointP = key_pub.pointQ * key_priv.d - if pointP.is_point_at_infinity(): - raise ValueError("Invalid ECDH point") - z = long_to_bytes(pointP.x, pointP.size_in_bytes()) - return z - - -def key_agreement(**kwargs): - """Perform a Diffie-Hellman key agreement. - - Keywords: - kdf (callable): - A key derivation function that accepts ``bytes`` as input and returns - ``bytes``. - static_priv (EccKey): - The local static private key. Optional. - static_pub (EccKey): - The static public key that belongs to the peer. Optional. - eph_priv (EccKey): - The local ephemeral private key, generated for this session. Optional. - eph_pub (EccKey): - The ephemeral public key, received from the peer for this session. Optional. - - At least two keys must be passed, of which one is a private key and one - a public key. - - Returns (bytes): - The derived secret key material. - """ - - static_priv = kwargs.get('static_priv', None) - static_pub = kwargs.get('static_pub', None) - eph_priv = kwargs.get('eph_priv', None) - eph_pub = kwargs.get('eph_pub', None) - kdf = kwargs.get('kdf', None) - - if kdf is None: - raise ValueError("'kdf' is mandatory") - - count_priv = 0 - count_pub = 0 - curve = None - - def check_curve(curve, key, name, private): - if not isinstance(key, EccKey): - raise TypeError("'%s' must be an ECC key" % name) - if private and not key.has_private(): - raise TypeError("'%s' must be a private ECC key" % name) - if curve is None: - curve = key.curve - elif curve != key.curve: - raise TypeError("'%s' is defined on an incompatible curve" % name) - return curve - - if static_priv is not None: - curve = check_curve(curve, static_priv, 'static_priv', True) - count_priv += 1 - - if static_pub is not None: - curve = check_curve(curve, static_pub, 'static_pub', False) - count_pub += 1 - - if eph_priv is not None: - curve = check_curve(curve, eph_priv, 'eph_priv', True) - count_priv += 1 - - if eph_pub is not None: - curve = check_curve(curve, eph_pub, 'eph_pub', False) - count_pub += 1 - - if (count_priv + count_pub) < 2 or count_priv == 0 or count_pub == 0: - raise ValueError("Too few keys for the ECDH key agreement") - - Zs = b'' - Ze = b'' - - if static_priv and static_pub: - # C(*, 2s) - Zs = _compute_ecdh(static_priv, static_pub) - - if eph_priv and eph_pub: - # C(2e, 0s) or C(2e, 2s) - if bool(static_priv) != bool(static_pub): - raise ValueError("DH mode C(2e, 1s) is not supported") - Ze = _compute_ecdh(eph_priv, eph_pub) - elif eph_priv and static_pub: - # C(1e, 2s) or C(1e, 1s) - Ze = _compute_ecdh(eph_priv, static_pub) - elif eph_pub and static_priv: - # C(1e, 2s) or C(1e, 1s) - Ze = _compute_ecdh(static_priv, eph_pub) - - Z = Ze + Zs - - return kdf(Z) diff --git a/resources/lib/deps/Cryptodome/Protocol/DH.pyi b/resources/lib/deps/Cryptodome/Protocol/DH.pyi deleted file mode 100644 index b1da888..0000000 --- a/resources/lib/deps/Cryptodome/Protocol/DH.pyi +++ /dev/null @@ -1,15 +0,0 @@ -from typing import TypedDict, Callable, TypeVar, Generic -from typing_extensions import Unpack, NotRequired - -from Cryptodome.PublicKey.ECC import EccKey - -T = TypeVar('T') - -class RequestParams(TypedDict, Generic[T]): - kdf: Callable[[bytes|bytearray|memoryview], T] - static_priv: NotRequired[EccKey] - static_pub: NotRequired[EccKey] - eph_priv: NotRequired[EccKey] - eph_pub: NotRequired[EccKey] - -def key_agreement(**kwargs: Unpack[RequestParams[T]]) -> T: ... diff --git a/resources/lib/deps/Cryptodome/Protocol/KDF.py b/resources/lib/deps/Cryptodome/Protocol/KDF.py deleted file mode 100644 index b6d747e..0000000 --- a/resources/lib/deps/Cryptodome/Protocol/KDF.py +++ /dev/null @@ -1,642 +0,0 @@ -# coding=utf-8 -# -# KDF.py : a collection of Key Derivation Functions -# -# Part of the Python Cryptography Toolkit -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -import re -import struct -from functools import reduce - -from Cryptodome.Util.py3compat import (tobytes, bord, _copy_bytes, iter_range, - tostr, bchr, bstr) - -from Cryptodome.Hash import SHA1, SHA256, HMAC, CMAC, BLAKE2s -from Cryptodome.Util.strxor import strxor -from Cryptodome.Random import get_random_bytes -from Cryptodome.Util.number import size as bit_size, long_to_bytes, bytes_to_long - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - create_string_buffer, - get_raw_buffer, c_size_t) - -_raw_salsa20_lib = load_pycryptodome_raw_lib("Cryptodome.Cipher._Salsa20", - """ - int Salsa20_8_core(const uint8_t *x, const uint8_t *y, - uint8_t *out); - """) - -_raw_scrypt_lib = load_pycryptodome_raw_lib("Cryptodome.Protocol._scrypt", - """ - typedef int (core_t)(const uint8_t [64], const uint8_t [64], uint8_t [64]); - int scryptROMix(const uint8_t *data_in, uint8_t *data_out, - size_t data_len, unsigned N, core_t *core); - """) - - -def PBKDF1(password, salt, dkLen, count=1000, hashAlgo=None): - """Derive one key from a password (or passphrase). - - This function performs key derivation according to an old version of - the PKCS#5 standard (v1.5) or `RFC2898 - `_. - - Args: - password (string): - The secret password to generate the key from. - salt (byte string): - An 8 byte string to use for better protection from dictionary attacks. - This value does not need to be kept secret, but it should be randomly - chosen for each derivation. - dkLen (integer): - The length of the desired key. The default is 16 bytes, suitable for - instance for :mod:`Cryptodome.Cipher.AES`. - count (integer): - The number of iterations to carry out. The recommendation is 1000 or - more. - hashAlgo (module): - The hash algorithm to use, as a module or an object from the :mod:`Cryptodome.Hash` package. - The digest length must be no shorter than ``dkLen``. - The default algorithm is :mod:`Cryptodome.Hash.SHA1`. - - Return: - A byte string of length ``dkLen`` that can be used as key. - """ - - if not hashAlgo: - hashAlgo = SHA1 - password = tobytes(password) - pHash = hashAlgo.new(password+salt) - digest = pHash.digest_size - if dkLen > digest: - raise TypeError("Selected hash algorithm has a too short digest (%d bytes)." % digest) - if len(salt) != 8: - raise ValueError("Salt is not 8 bytes long (%d bytes instead)." % len(salt)) - for i in iter_range(count-1): - pHash = pHash.new(pHash.digest()) - return pHash.digest()[:dkLen] - - -def PBKDF2(password, salt, dkLen=16, count=1000, prf=None, hmac_hash_module=None): - """Derive one or more keys from a password (or passphrase). - - This function performs key derivation according to the PKCS#5 standard (v2.0). - - Args: - password (string or byte string): - The secret password to generate the key from. - - Strings will be encoded as ISO 8859-1 (also known as Latin-1), - which does not allow any characters with codepoints > 255. - salt (string or byte string): - A (byte) string to use for better protection from dictionary attacks. - This value does not need to be kept secret, but it should be randomly - chosen for each derivation. It is recommended to use at least 16 bytes. - - Strings will be encoded as ISO 8859-1 (also known as Latin-1), - which does not allow any characters with codepoints > 255. - dkLen (integer): - The cumulative length of the keys to produce. - - Due to a flaw in the PBKDF2 design, you should not request more bytes - than the ``prf`` can output. For instance, ``dkLen`` should not exceed - 20 bytes in combination with ``HMAC-SHA1``. - count (integer): - The number of iterations to carry out. The higher the value, the slower - and the more secure the function becomes. - - You should find the maximum number of iterations that keeps the - key derivation still acceptable on the slowest hardware you must support. - - Although the default value is 1000, **it is recommended to use at least - 1000000 (1 million) iterations**. - prf (callable): - A pseudorandom function. It must be a function that returns a - pseudorandom byte string from two parameters: a secret and a salt. - The slower the algorithm, the more secure the derivation function. - If not specified, **HMAC-SHA1** is used. - hmac_hash_module (module): - A module from ``Cryptodome.Hash`` implementing a Merkle-Damgard cryptographic - hash, which PBKDF2 must use in combination with HMAC. - This parameter is mutually exclusive with ``prf``. - - Return: - A byte string of length ``dkLen`` that can be used as key material. - If you want multiple keys, just break up this string into segments of the desired length. - """ - - password = tobytes(password) - salt = tobytes(salt) - - if prf and hmac_hash_module: - raise ValueError("'prf' and 'hmac_hash_module' are mutually exlusive") - - if prf is None and hmac_hash_module is None: - hmac_hash_module = SHA1 - - if prf or not hasattr(hmac_hash_module, "_pbkdf2_hmac_assist"): - # Generic (and slow) implementation - - if prf is None: - prf = lambda p,s: HMAC.new(p, s, hmac_hash_module).digest() - - def link(s): - s[0], s[1] = s[1], prf(password, s[1]) - return s[0] - - key = b'' - i = 1 - while len(key) < dkLen: - s = [ prf(password, salt + struct.pack(">I", i)) ] * 2 - key += reduce(strxor, (link(s) for j in range(count)) ) - i += 1 - - else: - # Optimized implementation - key = b'' - i = 1 - while len(key)I", i)).digest() - key += base._pbkdf2_hmac_assist(first_digest, count) - i += 1 - - return key[:dkLen] - - -class _S2V(object): - """String-to-vector PRF as defined in `RFC5297`_. - - This class implements a pseudorandom function family - based on CMAC that takes as input a vector of strings. - - .. _RFC5297: http://tools.ietf.org/html/rfc5297 - """ - - def __init__(self, key, ciphermod, cipher_params=None): - """Initialize the S2V PRF. - - :Parameters: - key : byte string - A secret that can be used as key for CMACs - based on ciphers from ``ciphermod``. - ciphermod : module - A block cipher module from `Cryptodome.Cipher`. - cipher_params : dictionary - A set of extra parameters to use to create a cipher instance. - """ - - self._key = _copy_bytes(None, None, key) - self._ciphermod = ciphermod - self._last_string = self._cache = b'\x00' * ciphermod.block_size - - # Max number of update() call we can process - self._n_updates = ciphermod.block_size * 8 - 1 - - if cipher_params is None: - self._cipher_params = {} - else: - self._cipher_params = dict(cipher_params) - - @staticmethod - def new(key, ciphermod): - """Create a new S2V PRF. - - :Parameters: - key : byte string - A secret that can be used as key for CMACs - based on ciphers from ``ciphermod``. - ciphermod : module - A block cipher module from `Cryptodome.Cipher`. - """ - return _S2V(key, ciphermod) - - def _double(self, bs): - doubled = bytes_to_long(bs)<<1 - if bord(bs[0]) & 0x80: - doubled ^= 0x87 - return long_to_bytes(doubled, len(bs))[-len(bs):] - - def update(self, item): - """Pass the next component of the vector. - - The maximum number of components you can pass is equal to the block - length of the cipher (in bits) minus 1. - - :Parameters: - item : byte string - The next component of the vector. - :Raise TypeError: when the limit on the number of components has been reached. - """ - - if self._n_updates == 0: - raise TypeError("Too many components passed to S2V") - self._n_updates -= 1 - - mac = CMAC.new(self._key, - msg=self._last_string, - ciphermod=self._ciphermod, - cipher_params=self._cipher_params) - self._cache = strxor(self._double(self._cache), mac.digest()) - self._last_string = _copy_bytes(None, None, item) - - def derive(self): - """"Derive a secret from the vector of components. - - :Return: a byte string, as long as the block length of the cipher. - """ - - if len(self._last_string) >= 16: - # xorend - final = self._last_string[:-16] + strxor(self._last_string[-16:], self._cache) - else: - # zero-pad & xor - padded = (self._last_string + b'\x80' + b'\x00' * 15)[:16] - final = strxor(padded, self._double(self._cache)) - mac = CMAC.new(self._key, - msg=final, - ciphermod=self._ciphermod, - cipher_params=self._cipher_params) - return mac.digest() - - -def HKDF(master, key_len, salt, hashmod, num_keys=1, context=None): - """Derive one or more keys from a master secret using - the HMAC-based KDF defined in RFC5869_. - - Args: - master (byte string): - The unguessable value used by the KDF to generate the other keys. - It must be a high-entropy secret, though not necessarily uniform. - It must not be a password. - key_len (integer): - The length in bytes of every derived key. - salt (byte string): - A non-secret, reusable value that strengthens the randomness - extraction step. - Ideally, it is as long as the digest size of the chosen hash. - If empty, a string of zeroes in used. - hashmod (module): - A cryptographic hash algorithm from :mod:`Cryptodome.Hash`. - :mod:`Cryptodome.Hash.SHA512` is a good choice. - num_keys (integer): - The number of keys to derive. Every key is :data:`key_len` bytes long. - The maximum cumulative length of all keys is - 255 times the digest size. - context (byte string): - Optional identifier describing what the keys are used for. - - Return: - A byte string or a tuple of byte strings. - - .. _RFC5869: http://tools.ietf.org/html/rfc5869 - """ - - output_len = key_len * num_keys - if output_len > (255 * hashmod.digest_size): - raise ValueError("Too much secret data to derive") - if not salt: - salt = b'\x00' * hashmod.digest_size - if context is None: - context = b"" - - # Step 1: extract - hmac = HMAC.new(salt, master, digestmod=hashmod) - prk = hmac.digest() - - # Step 2: expand - t = [ b"" ] - n = 1 - tlen = 0 - while tlen < output_len: - hmac = HMAC.new(prk, t[-1] + context + struct.pack('B', n), digestmod=hashmod) - t.append(hmac.digest()) - tlen += hashmod.digest_size - n += 1 - derived_output = b"".join(t) - if num_keys == 1: - return derived_output[:key_len] - kol = [derived_output[idx:idx + key_len] - for idx in iter_range(0, output_len, key_len)] - return list(kol[:num_keys]) - - - -def scrypt(password, salt, key_len, N, r, p, num_keys=1): - """Derive one or more keys from a passphrase. - - Args: - password (string): - The secret pass phrase to generate the keys from. - salt (string): - A string to use for better protection from dictionary attacks. - This value does not need to be kept secret, - but it should be randomly chosen for each derivation. - It is recommended to be at least 16 bytes long. - key_len (integer): - The length in bytes of each derived key. - N (integer): - CPU/Memory cost parameter. It must be a power of 2 and less - than :math:`2^{32}`. - r (integer): - Block size parameter. - p (integer): - Parallelization parameter. - It must be no greater than :math:`(2^{32}-1)/(4r)`. - num_keys (integer): - The number of keys to derive. Every key is :data:`key_len` bytes long. - By default, only 1 key is generated. - The maximum cumulative length of all keys is :math:`(2^{32}-1)*32` - (that is, 128TB). - - A good choice of parameters *(N, r , p)* was suggested - by Colin Percival in his `presentation in 2009`__: - - - *( 2¹⁴, 8, 1 )* for interactive logins (≤100ms) - - *( 2²⁰, 8, 1 )* for file encryption (≤5s) - - Return: - A byte string or a tuple of byte strings. - - .. __: http://www.tarsnap.com/scrypt/scrypt-slides.pdf - """ - - if 2 ** (bit_size(N) - 1) != N: - raise ValueError("N must be a power of 2") - if N >= 2 ** 32: - raise ValueError("N is too big") - if p > ((2 ** 32 - 1) * 32) // (128 * r): - raise ValueError("p or r are too big") - - prf_hmac_sha256 = lambda p, s: HMAC.new(p, s, SHA256).digest() - - stage_1 = PBKDF2(password, salt, p * 128 * r, 1, prf=prf_hmac_sha256) - - scryptROMix = _raw_scrypt_lib.scryptROMix - core = _raw_salsa20_lib.Salsa20_8_core - - # Parallelize into p flows - data_out = [] - for flow in iter_range(p): - idx = flow * 128 * r - buffer_out = create_string_buffer(128 * r) - result = scryptROMix(stage_1[idx : idx + 128 * r], - buffer_out, - c_size_t(128 * r), - N, - core) - if result: - raise ValueError("Error %X while running scrypt" % result) - data_out += [ get_raw_buffer(buffer_out) ] - - dk = PBKDF2(password, - b"".join(data_out), - key_len * num_keys, 1, - prf=prf_hmac_sha256) - - if num_keys == 1: - return dk - - kol = [dk[idx:idx + key_len] - for idx in iter_range(0, key_len * num_keys, key_len)] - return kol - - -def _bcrypt_encode(data): - s = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" - - bits = [] - for c in data: - bits_c = bin(bord(c))[2:].zfill(8) - bits.append(bstr(bits_c)) - bits = b"".join(bits) - - bits6 = [ bits[idx:idx+6] for idx in range(0, len(bits), 6) ] - - result = [] - for g in bits6[:-1]: - idx = int(g, 2) - result.append(s[idx]) - - g = bits6[-1] - idx = int(g, 2) << (6 - len(g)) - result.append(s[idx]) - result = "".join(result) - - return tobytes(result) - - -def _bcrypt_decode(data): - s = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" - - bits = [] - for c in tostr(data): - idx = s.find(c) - bits6 = bin(idx)[2:].zfill(6) - bits.append(bits6) - bits = "".join(bits) - - modulo4 = len(data) % 4 - if modulo4 == 1: - raise ValueError("Incorrect length") - elif modulo4 == 2: - bits = bits[:-4] - elif modulo4 == 3: - bits = bits[:-2] - - bits8 = [ bits[idx:idx+8] for idx in range(0, len(bits), 8) ] - - result = [] - for g in bits8: - result.append(bchr(int(g, 2))) - result = b"".join(result) - - return result - - -def _bcrypt_hash(password, cost, salt, constant, invert): - from Cryptodome.Cipher import _EKSBlowfish - - if len(password) > 72: - raise ValueError("The password is too long. It must be 72 bytes at most.") - - if not (4 <= cost <= 31): - raise ValueError("bcrypt cost factor must be in the range 4..31") - - cipher = _EKSBlowfish.new(password, _EKSBlowfish.MODE_ECB, salt, cost, invert) - ctext = constant - for _ in range(64): - ctext = cipher.encrypt(ctext) - return ctext - - -def bcrypt(password, cost, salt=None): - """Hash a password into a key, using the OpenBSD bcrypt protocol. - - Args: - password (byte string or string): - The secret password or pass phrase. - It must be at most 72 bytes long. - It must not contain the zero byte. - Unicode strings will be encoded as UTF-8. - cost (integer): - The exponential factor that makes it slower to compute the hash. - It must be in the range 4 to 31. - A value of at least 12 is recommended. - salt (byte string): - Optional. Random byte string to thwarts dictionary and rainbow table - attacks. It must be 16 bytes long. - If not passed, a random value is generated. - - Return (byte string): - The bcrypt hash - - Raises: - ValueError: if password is longer than 72 bytes or if it contains the zero byte - - """ - - password = tobytes(password, "utf-8") - - if password.find(bchr(0)[0]) != -1: - raise ValueError("The password contains the zero byte") - - if len(password) < 72: - password += b"\x00" - - if salt is None: - salt = get_random_bytes(16) - if len(salt) != 16: - raise ValueError("bcrypt salt must be 16 bytes long") - - ctext = _bcrypt_hash(password, cost, salt, b"OrpheanBeholderScryDoubt", True) - - cost_enc = b"$" + bstr(str(cost).zfill(2)) - salt_enc = b"$" + _bcrypt_encode(salt) - hash_enc = _bcrypt_encode(ctext[:-1]) # only use 23 bytes, not 24 - return b"$2a" + cost_enc + salt_enc + hash_enc - - -def bcrypt_check(password, bcrypt_hash): - """Verify if the provided password matches the given bcrypt hash. - - Args: - password (byte string or string): - The secret password or pass phrase to test. - It must be at most 72 bytes long. - It must not contain the zero byte. - Unicode strings will be encoded as UTF-8. - bcrypt_hash (byte string, bytearray): - The reference bcrypt hash the password needs to be checked against. - - Raises: - ValueError: if the password does not match - """ - - bcrypt_hash = tobytes(bcrypt_hash) - - if len(bcrypt_hash) != 60: - raise ValueError("Incorrect length of the bcrypt hash: %d bytes instead of 60" % len(bcrypt_hash)) - - if bcrypt_hash[:4] != b'$2a$': - raise ValueError("Unsupported prefix") - - p = re.compile(br'\$2a\$([0-9][0-9])\$([A-Za-z0-9./]{22,22})([A-Za-z0-9./]{31,31})') - r = p.match(bcrypt_hash) - if not r: - raise ValueError("Incorrect bcrypt hash format") - - cost = int(r.group(1)) - if not (4 <= cost <= 31): - raise ValueError("Incorrect cost") - - salt = _bcrypt_decode(r.group(2)) - - bcrypt_hash2 = bcrypt(password, cost, salt) - - secret = get_random_bytes(16) - - mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=bcrypt_hash).digest() - mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=bcrypt_hash2).digest() - if mac1 != mac2: - raise ValueError("Incorrect bcrypt hash") - - -def SP800_108_Counter(master, key_len, prf, num_keys=None, label=b'', context=b''): - """Derive one or more keys from a master secret using - a pseudorandom function in Counter Mode, as specified in - `NIST SP 800-108r1 `_. - - Args: - master (byte string): - The secret value used by the KDF to derive the other keys. - It must not be a password. - The length on the secret must be consistent with the input expected by - the :data:`prf` function. - key_len (integer): - The length in bytes of each derived key. - prf (function): - A pseudorandom function that takes two byte strings as parameters: - the secret and an input. It returns another byte string. - num_keys (integer): - The number of keys to derive. Every key is :data:`key_len` bytes long. - By default, only 1 key is derived. - label (byte string): - Optional description of the purpose of the derived keys. - It must not contain zero bytes. - context (byte string): - Optional information pertaining to - the protocol that uses the keys, such as the identity of the - participants, nonces, session IDs, etc. - It must not contain zero bytes. - - Return: - - a byte string (if ``num_keys`` is not specified), or - - a tuple of byte strings (if ``num_key`` is specified). - """ - - if num_keys is None: - num_keys = 1 - - if label.find(b'\x00') != -1: - raise ValueError("Null byte found in label") - - if context.find(b'\x00') != -1: - raise ValueError("Null byte found in context") - - key_len_enc = long_to_bytes(key_len * num_keys * 8, 4) - output_len = key_len * num_keys - - i = 1 - dk = b"" - while len(dk) < output_len: - info = long_to_bytes(i, 4) + label + b'\x00' + context + key_len_enc - dk += prf(master, info) - i += 1 - if i > 0xFFFFFFFF: - raise ValueError("Overflow in SP800 108 counter") - - if num_keys == 1: - return dk[:key_len] - else: - kol = [dk[idx:idx + key_len] - for idx in iter_range(0, output_len, key_len)] - return kol diff --git a/resources/lib/deps/Cryptodome/Protocol/KDF.pyi b/resources/lib/deps/Cryptodome/Protocol/KDF.pyi deleted file mode 100644 index 745f019..0000000 --- a/resources/lib/deps/Cryptodome/Protocol/KDF.pyi +++ /dev/null @@ -1,42 +0,0 @@ -from types import ModuleType -from typing import Optional, Callable, Tuple, Union, Dict, Any, overload -from typing_extensions import Literal - -Buffer=bytes|bytearray|memoryview - -RNG = Callable[[int], bytes] -PRF = Callable[[bytes, bytes], bytes] - -def PBKDF1(password: str, salt: bytes, dkLen: int, count: Optional[int]=1000, hashAlgo: Optional[ModuleType]=None) -> bytes: ... -def PBKDF2(password: str, salt: bytes, dkLen: Optional[int]=16, count: Optional[int]=1000, prf: Optional[RNG]=None, hmac_hash_module: Optional[ModuleType]=None) -> bytes: ... - -class _S2V(object): - def __init__(self, key: bytes, ciphermod: ModuleType, cipher_params: Optional[Dict[Any, Any]]=None) -> None: ... - - @staticmethod - def new(key: bytes, ciphermod: ModuleType) -> None: ... - def update(self, item: bytes) -> None: ... - def derive(self) -> bytes: ... - -def HKDF(master: bytes, key_len: int, salt: bytes, hashmod: ModuleType, num_keys: Optional[int]=1, context: Optional[bytes]=None) -> Union[bytes, Tuple[bytes, ...]]: ... - -def scrypt(password: str, salt: str, key_len: int, N: int, r: int, p: int, num_keys: Optional[int]=1) -> Union[bytes, Tuple[bytes, ...]]: ... - -def _bcrypt_decode(data: bytes) -> bytes: ... -def _bcrypt_hash(password:bytes , cost: int, salt: bytes, constant:bytes, invert:bool) -> bytes: ... -def bcrypt(password: Union[bytes, str], cost: int, salt: Optional[bytes]=None) -> bytes: ... -def bcrypt_check(password: Union[bytes, str], bcrypt_hash: Union[bytes, bytearray, str]) -> None: ... - -@overload -def SP800_108_Counter(master: Buffer, - key_len: int, - prf: PRF, - num_keys: Literal[None] = None, - label: Buffer = b'', context: Buffer = b'') -> bytes: ... - -@overload -def SP800_108_Counter(master: Buffer, - key_len: int, - prf: PRF, - num_keys: int, - label: Buffer = b'', context: Buffer = b'') -> Tuple[bytes]: ... diff --git a/resources/lib/deps/Cryptodome/Protocol/SecretSharing.py b/resources/lib/deps/Cryptodome/Protocol/SecretSharing.py deleted file mode 100644 index 6fdc9b4..0000000 --- a/resources/lib/deps/Cryptodome/Protocol/SecretSharing.py +++ /dev/null @@ -1,278 +0,0 @@ -# -# SecretSharing.py : distribute a secret amongst a group of participants -# -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from Cryptodome.Util.py3compat import is_native_int -from Cryptodome.Util import number -from Cryptodome.Util.number import long_to_bytes, bytes_to_long -from Cryptodome.Random import get_random_bytes as rng - - -def _mult_gf2(f1, f2): - """Multiply two polynomials in GF(2)""" - - # Ensure f2 is the smallest - if f2 > f1: - f1, f2 = f2, f1 - z = 0 - while f2: - if f2 & 1: - z ^= f1 - f1 <<= 1 - f2 >>= 1 - return z - - -def _div_gf2(a, b): - """ - Compute division of polynomials over GF(2). - Given a and b, it finds two polynomials q and r such that: - - a = b*q + r with deg(r)= d: - s = 1 << (deg(r) - d) - q ^= s - r ^= _mult_gf2(b, s) - return (q, r) - - -class _Element(object): - """Element of GF(2^128) field""" - - # The irreducible polynomial defining this field is 1+x+x^2+x^7+x^128 - irr_poly = 1 + 2 + 4 + 128 + 2 ** 128 - - def __init__(self, encoded_value): - """Initialize the element to a certain value. - - The value passed as parameter is internally encoded as - a 128-bit integer, where each bit represents a polynomial - coefficient. The LSB is the constant coefficient. - """ - - if is_native_int(encoded_value): - self._value = encoded_value - elif len(encoded_value) == 16: - self._value = bytes_to_long(encoded_value) - else: - raise ValueError("The encoded value must be an integer or a 16 byte string") - - def __eq__(self, other): - return self._value == other._value - - def __int__(self): - """Return the field element, encoded as a 128-bit integer.""" - return self._value - - def encode(self): - """Return the field element, encoded as a 16 byte string.""" - return long_to_bytes(self._value, 16) - - def __mul__(self, factor): - - f1 = self._value - f2 = factor._value - - # Make sure that f2 is the smallest, to speed up the loop - if f2 > f1: - f1, f2 = f2, f1 - - if self.irr_poly in (f1, f2): - return _Element(0) - - mask1 = 2 ** 128 - v, z = f1, 0 - while f2: - # if f2 ^ 1: z ^= v - mask2 = int(bin(f2 & 1)[2:] * 128, base=2) - z = (mask2 & (z ^ v)) | ((mask1 - mask2 - 1) & z) - v <<= 1 - # if v & mask1: v ^= self.irr_poly - mask3 = int(bin((v >> 128) & 1)[2:] * 128, base=2) - v = (mask3 & (v ^ self.irr_poly)) | ((mask1 - mask3 - 1) & v) - f2 >>= 1 - return _Element(z) - - def __add__(self, term): - return _Element(self._value ^ term._value) - - def inverse(self): - """Return the inverse of this element in GF(2^128).""" - - # We use the Extended GCD algorithm - # http://en.wikipedia.org/wiki/Polynomial_greatest_common_divisor - - if self._value == 0: - raise ValueError("Inversion of zero") - - r0, r1 = self._value, self.irr_poly - s0, s1 = 1, 0 - while r1 > 0: - q = _div_gf2(r0, r1)[0] - r0, r1 = r1, r0 ^ _mult_gf2(q, r1) - s0, s1 = s1, s0 ^ _mult_gf2(q, s1) - return _Element(s0) - - def __pow__(self, exponent): - result = _Element(self._value) - for _ in range(exponent - 1): - result = result * self - return result - - -class Shamir(object): - """Shamir's secret sharing scheme. - - A secret is split into ``n`` shares, and it is sufficient to collect - ``k`` of them to reconstruct the secret. - """ - - @staticmethod - def split(k, n, secret, ssss=False): - """Split a secret into ``n`` shares. - - The secret can be reconstructed later using just ``k`` shares - out of the original ``n``. - Each share must be kept confidential to the person it was - assigned to. - - Each share is associated to an index (starting from 1). - - Args: - k (integer): - The sufficient number of shares to reconstruct the secret (``k < n``). - n (integer): - The number of shares that this method will create. - secret (byte string): - A byte string of 16 bytes (e.g. the AES 128 key). - ssss (bool): - If ``True``, the shares can be used with the ``ssss`` utility. - Default: ``False``. - - Return (tuples): - ``n`` tuples. A tuple is meant for each participant and it contains two items: - - 1. the unique index (an integer) - 2. the share (a byte string, 16 bytes) - """ - - # - # We create a polynomial with random coefficients in GF(2^128): - # - # p(x) = \sum_{i=0}^{k-1} c_i * x^i - # - # c_0 is the encoded secret - # - - coeffs = [_Element(rng(16)) for i in range(k - 1)] - coeffs.append(_Element(secret)) - - # Each share is y_i = p(x_i) where x_i is the public index - # associated to each of the n users. - - def make_share(user, coeffs, ssss): - idx = _Element(user) - share = _Element(0) - for coeff in coeffs: - share = idx * share + coeff - if ssss: - share += _Element(user) ** len(coeffs) - return share.encode() - - return [(i, make_share(i, coeffs, ssss)) for i in range(1, n + 1)] - - @staticmethod - def combine(shares, ssss=False): - """Recombine a secret, if enough shares are presented. - - Args: - shares (tuples): - The *k* tuples, each containin the index (an integer) and - the share (a byte string, 16 bytes long) that were assigned to - a participant. - ssss (bool): - If ``True``, the shares were produced by the ``ssss`` utility. - Default: ``False``. - - Return: - The original secret, as a byte string (16 bytes long). - """ - - # - # Given k points (x,y), the interpolation polynomial of degree k-1 is: - # - # L(x) = \sum_{j=0}^{k-1} y_i * l_j(x) - # - # where: - # - # l_j(x) = \prod_{ \overset{0 \le m \le k-1}{m \ne j} } - # \frac{x - x_m}{x_j - x_m} - # - # However, in this case we are purely interested in the constant - # coefficient of L(x). - # - - k = len(shares) - - gf_shares = [] - for x in shares: - idx = _Element(x[0]) - value = _Element(x[1]) - if any(y[0] == idx for y in gf_shares): - raise ValueError("Duplicate share") - if ssss: - value += idx ** k - gf_shares.append((idx, value)) - - result = _Element(0) - for j in range(k): - x_j, y_j = gf_shares[j] - - numerator = _Element(1) - denominator = _Element(1) - - for m in range(k): - x_m = gf_shares[m][0] - if m != j: - numerator *= x_m - denominator *= x_j + x_m - result += y_j * numerator * denominator.inverse() - return result.encode() diff --git a/resources/lib/deps/Cryptodome/Protocol/SecretSharing.pyi b/resources/lib/deps/Cryptodome/Protocol/SecretSharing.pyi deleted file mode 100644 index 5952c99..0000000 --- a/resources/lib/deps/Cryptodome/Protocol/SecretSharing.pyi +++ /dev/null @@ -1,22 +0,0 @@ -from typing import Union, List, Tuple, Optional - -def _mult_gf2(f1: int, f2: int) -> int : ... -def _div_gf2(a: int, b: int) -> int : ... - -class _Element(object): - irr_poly: int - def __init__(self, encoded_value: Union[int, bytes]) -> None: ... - def __eq__(self, other) -> bool: ... - def __int__(self) -> int: ... - def encode(self) -> bytes: ... - def __mul__(self, factor: int) -> _Element: ... - def __add__(self, term: _Element) -> _Element: ... - def inverse(self) -> _Element: ... - def __pow__(self, exponent) -> _Element: ... - -class Shamir(object): - @staticmethod - def split(k: int, n: int, secret: bytes, ssss: Optional[bool]) -> List[Tuple[int, bytes]]: ... - @staticmethod - def combine(shares: List[Tuple[int, bytes]], ssss: Optional[bool]) -> bytes: ... - diff --git a/resources/lib/deps/Cryptodome/Protocol/__init__.py b/resources/lib/deps/Cryptodome/Protocol/__init__.py deleted file mode 100644 index 76e22bf..0000000 --- a/resources/lib/deps/Cryptodome/Protocol/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -__all__ = ['KDF', 'SecretSharing', 'DH'] diff --git a/resources/lib/deps/Cryptodome/Protocol/__init__.pyi b/resources/lib/deps/Cryptodome/Protocol/__init__.pyi deleted file mode 100644 index 377ed90..0000000 --- a/resources/lib/deps/Cryptodome/Protocol/__init__.pyi +++ /dev/null @@ -1 +0,0 @@ -__all__ = ['KDF.pyi', 'SecretSharing.pyi'] diff --git a/resources/lib/deps/Cryptodome/Protocol/_scrypt.abi3.so b/resources/lib/deps/Cryptodome/Protocol/_scrypt.abi3.so deleted file mode 100755 index baf83a8478584e1d98561442002e0c6edb23b8eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26176 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 diff --git a/resources/lib/deps/Cryptodome/PublicKey/DSA.py b/resources/lib/deps/Cryptodome/PublicKey/DSA.py deleted file mode 100644 index dddd304..0000000 --- a/resources/lib/deps/Cryptodome/PublicKey/DSA.py +++ /dev/null @@ -1,682 +0,0 @@ -# -*- coding: utf-8 -*- -# -# PublicKey/DSA.py : DSA signature primitive -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -__all__ = ['generate', 'construct', 'DsaKey', 'import_key' ] - -import binascii -import struct -import itertools - -from Cryptodome.Util.py3compat import bchr, bord, tobytes, tostr, iter_range - -from Cryptodome import Random -from Cryptodome.IO import PKCS8, PEM -from Cryptodome.Hash import SHA256 -from Cryptodome.Util.asn1 import ( - DerObject, DerSequence, - DerInteger, DerObjectId, - DerBitString, - ) - -from Cryptodome.Math.Numbers import Integer -from Cryptodome.Math.Primality import (test_probable_prime, COMPOSITE, - PROBABLY_PRIME) - -from Cryptodome.PublicKey import (_expand_subject_public_key_info, - _create_subject_public_key_info, - _extract_subject_public_key_info) - -# ; The following ASN.1 types are relevant for DSA -# -# SubjectPublicKeyInfo ::= SEQUENCE { -# algorithm AlgorithmIdentifier, -# subjectPublicKey BIT STRING -# } -# -# id-dsa ID ::= { iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 1 } -# -# ; See RFC3279 -# Dss-Parms ::= SEQUENCE { -# p INTEGER, -# q INTEGER, -# g INTEGER -# } -# -# DSAPublicKey ::= INTEGER -# -# DSSPrivatKey_OpenSSL ::= SEQUENCE -# version INTEGER, -# p INTEGER, -# q INTEGER, -# g INTEGER, -# y INTEGER, -# x INTEGER -# } -# - -class DsaKey(object): - r"""Class defining an actual DSA key. - Do not instantiate directly. - Use :func:`generate`, :func:`construct` or :func:`import_key` instead. - - :ivar p: DSA modulus - :vartype p: integer - - :ivar q: Order of the subgroup - :vartype q: integer - - :ivar g: Generator - :vartype g: integer - - :ivar y: Public key - :vartype y: integer - - :ivar x: Private key - :vartype x: integer - - :undocumented: exportKey, publickey - """ - - _keydata = ['y', 'g', 'p', 'q', 'x'] - - def __init__(self, key_dict): - input_set = set(key_dict.keys()) - public_set = set(('y' , 'g', 'p', 'q')) - if not public_set.issubset(input_set): - raise ValueError("Some DSA components are missing = %s" % - str(public_set - input_set)) - extra_set = input_set - public_set - if extra_set and extra_set != set(('x',)): - raise ValueError("Unknown DSA components = %s" % - str(extra_set - set(('x',)))) - self._key = dict(key_dict) - - def _sign(self, m, k): - if not self.has_private(): - raise TypeError("DSA public key cannot be used for signing") - if not (1 < k < self.q): - raise ValueError("k is not between 2 and q-1") - - x, q, p, g = [self._key[comp] for comp in ['x', 'q', 'p', 'g']] - - blind_factor = Integer.random_range(min_inclusive=1, - max_exclusive=q) - inv_blind_k = (blind_factor * k).inverse(q) - blind_x = x * blind_factor - - r = pow(g, k, p) % q # r = (g**k mod p) mod q - s = (inv_blind_k * (blind_factor * m + blind_x * r)) % q - return map(int, (r, s)) - - def _verify(self, m, sig): - r, s = sig - y, q, p, g = [self._key[comp] for comp in ['y', 'q', 'p', 'g']] - if not (0 < r < q) or not (0 < s < q): - return False - w = Integer(s).inverse(q) - u1 = (w * m) % q - u2 = (w * r) % q - v = (pow(g, u1, p) * pow(y, u2, p) % p) % q - return v == r - - def has_private(self): - """Whether this is a DSA private key""" - - return 'x' in self._key - - def can_encrypt(self): # legacy - return False - - def can_sign(self): # legacy - return True - - def public_key(self): - """A matching DSA public key. - - Returns: - a new :class:`DsaKey` object - """ - - public_components = dict((k, self._key[k]) for k in ('y', 'g', 'p', 'q')) - return DsaKey(public_components) - - def __eq__(self, other): - if bool(self.has_private()) != bool(other.has_private()): - return False - - result = True - for comp in self._keydata: - result = result and (getattr(self._key, comp, None) == - getattr(other._key, comp, None)) - return result - - def __ne__(self, other): - return not self.__eq__(other) - - def __getstate__(self): - # DSA key is not pickable - from pickle import PicklingError - raise PicklingError - - def domain(self): - """The DSA domain parameters. - - Returns - tuple : (p,q,g) - """ - - return [int(self._key[comp]) for comp in ('p', 'q', 'g')] - - def __repr__(self): - attrs = [] - for k in self._keydata: - if k == 'p': - bits = Integer(self.p).size_in_bits() - attrs.append("p(%d)" % (bits,)) - elif hasattr(self, k): - attrs.append(k) - if self.has_private(): - attrs.append("private") - # PY3K: This is meant to be text, do not change to bytes (data) - return "<%s @0x%x %s>" % (self.__class__.__name__, id(self), ",".join(attrs)) - - def __getattr__(self, item): - try: - return int(self._key[item]) - except KeyError: - raise AttributeError(item) - - def export_key(self, format='PEM', pkcs8=None, passphrase=None, - protection=None, randfunc=None): - """Export this DSA key. - - Args: - format (string): - The encoding for the output: - - - *'PEM'* (default). ASCII as per `RFC1421`_/ `RFC1423`_. - - *'DER'*. Binary ASN.1 encoding. - - *'OpenSSH'*. ASCII one-liner as per `RFC4253`_. - Only suitable for public keys, not for private keys. - - passphrase (string): - *Private keys only*. The pass phrase to protect the output. - - pkcs8 (boolean): - *Private keys only*. If ``True`` (default), the key is encoded - with `PKCS#8`_. If ``False``, it is encoded in the custom - OpenSSL/OpenSSH container. - - protection (string): - *Only in combination with a pass phrase*. - The encryption scheme to use to protect the output. - - If :data:`pkcs8` takes value ``True``, this is the PKCS#8 - algorithm to use for deriving the secret and encrypting - the private DSA key. - For a complete list of algorithms, see :mod:`Cryptodome.IO.PKCS8`. - The default is *PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC*. - - If :data:`pkcs8` is ``False``, the obsolete PEM encryption scheme is - used. It is based on MD5 for key derivation, and Triple DES for - encryption. Parameter :data:`protection` is then ignored. - - The combination ``format='DER'`` and ``pkcs8=False`` is not allowed - if a passphrase is present. - - randfunc (callable): - A function that returns random bytes. - By default it is :func:`Cryptodome.Random.get_random_bytes`. - - Returns: - byte string : the encoded key - - Raises: - ValueError : when the format is unknown or when you try to encrypt a private - key with *DER* format and OpenSSL/OpenSSH. - - .. warning:: - If you don't provide a pass phrase, the private key will be - exported in the clear! - - .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt - .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt - .. _RFC4253: http://www.ietf.org/rfc/rfc4253.txt - .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt - """ - - if passphrase is not None: - passphrase = tobytes(passphrase) - - if randfunc is None: - randfunc = Random.get_random_bytes - - if format == 'OpenSSH': - tup1 = [self._key[x].to_bytes() for x in ('p', 'q', 'g', 'y')] - - def func(x): - if (bord(x[0]) & 0x80): - return bchr(0) + x - else: - return x - - tup2 = [func(x) for x in tup1] - keyparts = [b'ssh-dss'] + tup2 - keystring = b''.join( - [struct.pack(">I", len(kp)) + kp for kp in keyparts] - ) - return b'ssh-dss ' + binascii.b2a_base64(keystring)[:-1] - - # DER format is always used, even in case of PEM, which simply - # encodes it into BASE64. - params = DerSequence([self.p, self.q, self.g]) - if self.has_private(): - if pkcs8 is None: - pkcs8 = True - if pkcs8: - if not protection: - protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' - private_key = DerInteger(self.x).encode() - binary_key = PKCS8.wrap( - private_key, oid, passphrase, - protection, key_params=params, - randfunc=randfunc - ) - if passphrase: - key_type = 'ENCRYPTED PRIVATE' - else: - key_type = 'PRIVATE' - passphrase = None - else: - if format != 'PEM' and passphrase: - raise ValueError("DSA private key cannot be encrypted") - ints = [0, self.p, self.q, self.g, self.y, self.x] - binary_key = DerSequence(ints).encode() - key_type = "DSA PRIVATE" - else: - if pkcs8: - raise ValueError("PKCS#8 is only meaningful for private keys") - - binary_key = _create_subject_public_key_info(oid, - DerInteger(self.y), params) - key_type = "PUBLIC" - - if format == 'DER': - return binary_key - if format == 'PEM': - pem_str = PEM.encode( - binary_key, key_type + " KEY", - passphrase, randfunc - ) - return tobytes(pem_str) - raise ValueError("Unknown key format '%s'. Cannot export the DSA key." % format) - - # Backward-compatibility - exportKey = export_key - publickey = public_key - - # Methods defined in PyCryptodome that we don't support anymore - - def sign(self, M, K): - raise NotImplementedError("Use module Cryptodome.Signature.DSS instead") - - def verify(self, M, signature): - raise NotImplementedError("Use module Cryptodome.Signature.DSS instead") - - def encrypt(self, plaintext, K): - raise NotImplementedError - - def decrypt(self, ciphertext): - raise NotImplementedError - - def blind(self, M, B): - raise NotImplementedError - - def unblind(self, M, B): - raise NotImplementedError - - def size(self): - raise NotImplementedError - - -def _generate_domain(L, randfunc): - """Generate a new set of DSA domain parameters""" - - N = { 1024:160, 2048:224, 3072:256 }.get(L) - if N is None: - raise ValueError("Invalid modulus length (%d)" % L) - - outlen = SHA256.digest_size * 8 - n = (L + outlen - 1) // outlen - 1 # ceil(L/outlen) -1 - b_ = L - 1 - (n * outlen) - - # Generate q (A.1.1.2) - q = Integer(4) - upper_bit = 1 << (N - 1) - while test_probable_prime(q, randfunc) != PROBABLY_PRIME: - seed = randfunc(64) - U = Integer.from_bytes(SHA256.new(seed).digest()) & (upper_bit - 1) - q = U | upper_bit | 1 - - assert(q.size_in_bits() == N) - - # Generate p (A.1.1.2) - offset = 1 - upper_bit = 1 << (L - 1) - while True: - V = [ SHA256.new(seed + Integer(offset + j).to_bytes()).digest() - for j in iter_range(n + 1) ] - V = [ Integer.from_bytes(v) for v in V ] - W = sum([V[i] * (1 << (i * outlen)) for i in iter_range(n)], - (V[n] & ((1 << b_) - 1)) * (1 << (n * outlen))) - - X = Integer(W + upper_bit) # 2^{L-1} < X < 2^{L} - assert(X.size_in_bits() == L) - - c = X % (q * 2) - p = X - (c - 1) # 2q divides (p-1) - if p.size_in_bits() == L and \ - test_probable_prime(p, randfunc) == PROBABLY_PRIME: - break - offset += n + 1 - - # Generate g (A.2.3, index=1) - e = (p - 1) // q - for count in itertools.count(1): - U = seed + b"ggen" + bchr(1) + Integer(count).to_bytes() - W = Integer.from_bytes(SHA256.new(U).digest()) - g = pow(W, e, p) - if g != 1: - break - - return (p, q, g, seed) - - -def generate(bits, randfunc=None, domain=None): - """Generate a new DSA key pair. - - The algorithm follows Appendix A.1/A.2 and B.1 of `FIPS 186-4`_, - respectively for domain generation and key pair generation. - - Args: - bits (integer): - Key length, or size (in bits) of the DSA modulus *p*. - It must be 1024, 2048 or 3072. - - randfunc (callable): - Random number generation function; it accepts a single integer N - and return a string of random data N bytes long. - If not specified, :func:`Cryptodome.Random.get_random_bytes` is used. - - domain (tuple): - The DSA domain parameters *p*, *q* and *g* as a list of 3 - integers. Size of *p* and *q* must comply to `FIPS 186-4`_. - If not specified, the parameters are created anew. - - Returns: - :class:`DsaKey` : a new DSA key object - - Raises: - ValueError : when **bits** is too little, too big, or not a multiple of 64. - - .. _FIPS 186-4: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf - """ - - if randfunc is None: - randfunc = Random.get_random_bytes - - if domain: - p, q, g = map(Integer, domain) - - ## Perform consistency check on domain parameters - # P and Q must be prime - fmt_error = test_probable_prime(p) == COMPOSITE - fmt_error |= test_probable_prime(q) == COMPOSITE - # Verify Lagrange's theorem for sub-group - fmt_error |= ((p - 1) % q) != 0 - fmt_error |= g <= 1 or g >= p - fmt_error |= pow(g, q, p) != 1 - if fmt_error: - raise ValueError("Invalid DSA domain parameters") - else: - p, q, g, _ = _generate_domain(bits, randfunc) - - L = p.size_in_bits() - N = q.size_in_bits() - - if L != bits: - raise ValueError("Mismatch between size of modulus (%d)" - " and 'bits' parameter (%d)" % (L, bits)) - - if (L, N) not in [(1024, 160), (2048, 224), - (2048, 256), (3072, 256)]: - raise ValueError("Lengths of p and q (%d, %d) are not compatible" - "to FIPS 186-3" % (L, N)) - - if not 1 < g < p: - raise ValueError("Incorrent DSA generator") - - # B.1.1 - c = Integer.random(exact_bits=N + 64, randfunc=randfunc) - x = c % (q - 1) + 1 # 1 <= x <= q-1 - y = pow(g, x, p) - - key_dict = { 'y':y, 'g':g, 'p':p, 'q':q, 'x':x } - return DsaKey(key_dict) - - -def construct(tup, consistency_check=True): - """Construct a DSA key from a tuple of valid DSA components. - - Args: - tup (tuple): - A tuple of long integers, with 4 or 5 items - in the following order: - - 1. Public key (*y*). - 2. Sub-group generator (*g*). - 3. Modulus, finite field order (*p*). - 4. Sub-group order (*q*). - 5. Private key (*x*). Optional. - - consistency_check (boolean): - If ``True``, the library will verify that the provided components - fulfil the main DSA properties. - - Raises: - ValueError: when the key being imported fails the most basic DSA validity checks. - - Returns: - :class:`DsaKey` : a DSA key object - """ - - key_dict = dict(zip(('y', 'g', 'p', 'q', 'x'), map(Integer, tup))) - key = DsaKey(key_dict) - - fmt_error = False - if consistency_check: - # P and Q must be prime - fmt_error = test_probable_prime(key.p) == COMPOSITE - fmt_error |= test_probable_prime(key.q) == COMPOSITE - # Verify Lagrange's theorem for sub-group - fmt_error |= ((key.p - 1) % key.q) != 0 - fmt_error |= key.g <= 1 or key.g >= key.p - fmt_error |= pow(key.g, key.q, key.p) != 1 - # Public key - fmt_error |= key.y <= 0 or key.y >= key.p - if hasattr(key, 'x'): - fmt_error |= key.x <= 0 or key.x >= key.q - fmt_error |= pow(key.g, key.x, key.p) != key.y - - if fmt_error: - raise ValueError("Invalid DSA key components") - - return key - - -# Dss-Parms ::= SEQUENCE { -# p OCTET STRING, -# q OCTET STRING, -# g OCTET STRING -# } -# DSAPublicKey ::= INTEGER -- public key, y - -def _import_openssl_private(encoded, passphrase, params): - if params: - raise ValueError("DSA private key already comes with parameters") - der = DerSequence().decode(encoded, nr_elements=6, only_ints_expected=True) - if der[0] != 0: - raise ValueError("No version found") - tup = [der[comp] for comp in (4, 3, 1, 2, 5)] - return construct(tup) - - -def _import_subjectPublicKeyInfo(encoded, passphrase, params): - - algoid, encoded_key, emb_params = _expand_subject_public_key_info(encoded) - if algoid != oid: - raise ValueError("No DSA subjectPublicKeyInfo") - if params and emb_params: - raise ValueError("Too many DSA parameters") - - y = DerInteger().decode(encoded_key).value - p, q, g = list(DerSequence().decode(params or emb_params)) - tup = (y, g, p, q) - return construct(tup) - - -def _import_x509_cert(encoded, passphrase, params): - - sp_info = _extract_subject_public_key_info(encoded) - return _import_subjectPublicKeyInfo(sp_info, None, params) - - -def _import_pkcs8(encoded, passphrase, params): - if params: - raise ValueError("PKCS#8 already includes parameters") - k = PKCS8.unwrap(encoded, passphrase) - if k[0] != oid: - raise ValueError("No PKCS#8 encoded DSA key") - x = DerInteger().decode(k[1]).value - p, q, g = list(DerSequence().decode(k[2])) - tup = (pow(g, x, p), g, p, q, x) - return construct(tup) - - -def _import_key_der(key_data, passphrase, params): - """Import a DSA key (public or private half), encoded in DER form.""" - - decodings = (_import_openssl_private, - _import_subjectPublicKeyInfo, - _import_x509_cert, - _import_pkcs8) - - for decoding in decodings: - try: - return decoding(key_data, passphrase, params) - except ValueError: - pass - - raise ValueError("DSA key format is not supported") - - -def import_key(extern_key, passphrase=None): - """Import a DSA key. - - Args: - extern_key (string or byte string): - The DSA key to import. - - The following formats are supported for a DSA **public** key: - - - X.509 certificate (binary DER or PEM) - - X.509 ``subjectPublicKeyInfo`` (binary DER or PEM) - - OpenSSH (ASCII one-liner, see `RFC4253`_) - - The following formats are supported for a DSA **private** key: - - - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` - DER SEQUENCE (binary or PEM) - - OpenSSL/OpenSSH custom format (binary or PEM) - - For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. - - passphrase (string): - In case of an encrypted private key, this is the pass phrase - from which the decryption key is derived. - - Encryption may be applied either at the `PKCS#8`_ or at the PEM level. - - Returns: - :class:`DsaKey` : a DSA key object - - Raises: - ValueError : when the given key cannot be parsed (possibly because - the pass phrase is wrong). - - .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt - .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt - .. _RFC4253: http://www.ietf.org/rfc/rfc4253.txt - .. _PKCS#8: http://www.ietf.org/rfc/rfc5208.txt - """ - - extern_key = tobytes(extern_key) - if passphrase is not None: - passphrase = tobytes(passphrase) - - if extern_key.startswith(b'-----'): - # This is probably a PEM encoded key - (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) - if enc_flag: - passphrase = None - return _import_key_der(der, passphrase, None) - - if extern_key.startswith(b'ssh-dss '): - # This is probably a public OpenSSH key - keystring = binascii.a2b_base64(extern_key.split(b' ')[1]) - keyparts = [] - while len(keystring) > 4: - length = struct.unpack(">I", keystring[:4])[0] - keyparts.append(keystring[4:4 + length]) - keystring = keystring[4 + length:] - if keyparts[0] == b"ssh-dss": - tup = [Integer.from_bytes(keyparts[x]) for x in (4, 3, 1, 2)] - return construct(tup) - - if len(extern_key) > 0 and bord(extern_key[0]) == 0x30: - # This is probably a DER encoded key - return _import_key_der(extern_key, passphrase, None) - - raise ValueError("DSA key format is not supported") - - -# Backward compatibility -importKey = import_key - -#: `Object ID`_ for a DSA key. -#: -#: id-dsa ID ::= { iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 1 } -#: -#: .. _`Object ID`: http://www.alvestrand.no/objectid/1.2.840.10040.4.1.html -oid = "1.2.840.10040.4.1" diff --git a/resources/lib/deps/Cryptodome/PublicKey/DSA.pyi b/resources/lib/deps/Cryptodome/PublicKey/DSA.pyi deleted file mode 100644 index 354ac1f..0000000 --- a/resources/lib/deps/Cryptodome/PublicKey/DSA.pyi +++ /dev/null @@ -1,31 +0,0 @@ -from typing import Dict, Tuple, Callable, Union, Optional - -__all__ = ['generate', 'construct', 'DsaKey', 'import_key' ] - -RNG = Callable[[int], bytes] - -class DsaKey(object): - def __init__(self, key_dict: Dict[str, int]) -> None: ... - def has_private(self) -> bool: ... - def can_encrypt(self) -> bool: ... # legacy - def can_sign(self) -> bool: ... # legacy - def public_key(self) -> DsaKey: ... - def __eq__(self, other: object) -> bool: ... - def __ne__(self, other: object) -> bool: ... - def __getstate__(self) -> None: ... - def domain(self) -> Tuple[int, int, int]: ... - def __repr__(self) -> str: ... - def __getattr__(self, item: str) -> int: ... - def export_key(self, format: Optional[str]="PEM", pkcs8: Optional[bool]=None, passphrase: Optional[str]=None, - protection: Optional[str]=None, randfunc: Optional[RNG]=None) -> bytes: ... - # Backward-compatibility - exportKey = export_key - publickey = public_key - -def generate(bits: int, randfunc: Optional[RNG]=None, domain: Optional[Tuple[int, int, int]]=None) -> DsaKey: ... -def construct(tup: Union[Tuple[int, int, int, int], Tuple[int, int, int, int, int]], consistency_check: Optional[bool]=True) -> DsaKey: ... -def import_key(extern_key: Union[str, bytes], passphrase: Optional[str]=None) -> DsaKey: ... -# Backward compatibility -importKey = import_key - -oid: str diff --git a/resources/lib/deps/Cryptodome/PublicKey/ECC.py b/resources/lib/deps/Cryptodome/PublicKey/ECC.py deleted file mode 100644 index 4546742..0000000 --- a/resources/lib/deps/Cryptodome/PublicKey/ECC.py +++ /dev/null @@ -1,1818 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2015, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from __future__ import print_function - -import re -import struct -import binascii -from collections import namedtuple - -from Cryptodome.Util.py3compat import bord, tobytes, tostr, bchr, is_string -from Cryptodome.Util.number import bytes_to_long, long_to_bytes - -from Cryptodome.Math.Numbers import Integer -from Cryptodome.Util.asn1 import (DerObjectId, DerOctetString, DerSequence, - DerBitString) - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, - SmartPointer, c_size_t, c_uint8_ptr, - c_ulonglong, null_pointer) - -from Cryptodome.PublicKey import (_expand_subject_public_key_info, - _create_subject_public_key_info, - _extract_subject_public_key_info) - -from Cryptodome.Hash import SHA512, SHAKE256 - -from Cryptodome.Random import get_random_bytes -from Cryptodome.Random.random import getrandbits - - -_ec_lib = load_pycryptodome_raw_lib("Cryptodome.PublicKey._ec_ws", """ -typedef void EcContext; -typedef void EcPoint; -int ec_ws_new_context(EcContext **pec_ctx, - const uint8_t *modulus, - const uint8_t *b, - const uint8_t *order, - size_t len, - uint64_t seed); -void ec_free_context(EcContext *ec_ctx); -int ec_ws_new_point(EcPoint **pecp, - const uint8_t *x, - const uint8_t *y, - size_t len, - const EcContext *ec_ctx); -void ec_ws_free_point(EcPoint *ecp); -int ec_ws_get_xy(uint8_t *x, - uint8_t *y, - size_t len, - const EcPoint *ecp); -int ec_ws_double(EcPoint *p); -int ec_ws_add(EcPoint *ecpa, EcPoint *ecpb); -int ec_ws_scalar(EcPoint *ecp, - const uint8_t *k, - size_t len, - uint64_t seed); -int ec_ws_clone(EcPoint **pecp2, const EcPoint *ecp); -int ec_ws_cmp(const EcPoint *ecp1, const EcPoint *ecp2); -int ec_ws_neg(EcPoint *p); -""") - -_ed25519_lib = load_pycryptodome_raw_lib("Cryptodome.PublicKey._ed25519", """ -typedef void Point; -int ed25519_new_point(Point **out, - const uint8_t x[32], - const uint8_t y[32], - size_t modsize, - const void *context); -int ed25519_clone(Point **P, const Point *Q); -void ed25519_free_point(Point *p); -int ed25519_cmp(const Point *p1, const Point *p2); -int ed25519_neg(Point *p); -int ed25519_get_xy(uint8_t *xb, uint8_t *yb, size_t modsize, Point *p); -int ed25519_double(Point *p); -int ed25519_add(Point *P1, const Point *P2); -int ed25519_scalar(Point *P, const uint8_t *scalar, size_t scalar_len, uint64_t seed); -""") - -_ed448_lib = load_pycryptodome_raw_lib("Cryptodome.PublicKey._ed448", """ -typedef void EcContext; -typedef void PointEd448; -int ed448_new_context(EcContext **pec_ctx); -void ed448_context(EcContext *ec_ctx); -void ed448_free_context(EcContext *ec_ctx); -int ed448_new_point(PointEd448 **out, - const uint8_t x[56], - const uint8_t y[56], - size_t len, - const EcContext *context); -int ed448_clone(PointEd448 **P, const PointEd448 *Q); -void ed448_free_point(PointEd448 *p); -int ed448_cmp(const PointEd448 *p1, const PointEd448 *p2); -int ed448_neg(PointEd448 *p); -int ed448_get_xy(uint8_t *xb, uint8_t *yb, size_t len, const PointEd448 *p); -int ed448_double(PointEd448 *p); -int ed448_add(PointEd448 *P1, const PointEd448 *P2); -int ed448_scalar(PointEd448 *P, const uint8_t *scalar, size_t scalar_len, uint64_t seed); -""") - - -def lib_func(ecc_obj, func_name): - if ecc_obj._curve.desc == "Ed25519": - result = getattr(_ed25519_lib, "ed25519_" + func_name) - elif ecc_obj._curve.desc == "Ed448": - result = getattr(_ed448_lib, "ed448_" + func_name) - else: - result = getattr(_ec_lib, "ec_ws_" + func_name) - return result - -# -# _curves is a database of curve parameters. Items are indexed by their -# human-friendly name, suchas "P-256". Each item has the following fields: -# - p: the prime number that defines the finite field for all modulo operations -# - b: the constant in the Short Weierstrass curve equation -# - order: the number of elements in the group with the generator below -# - Gx the affine coordinate X of the generator point -# - Gy the affine coordinate Y of the generator point -# - G the generator, as an EccPoint object -# - modulus_bits the minimum number of bits for encoding the modulus p -# - oid an ASCII string with the registered ASN.1 Object ID -# - context a raw pointer to memory holding a context for all curve operations (can be NULL) -# - desc an ASCII string describing the curve -# - openssh the ASCII string used in OpenSSH id files for public keys on this curve -# - name the ASCII string which is also a valid key in _curves - - -_Curve = namedtuple("_Curve", "p b order Gx Gy G modulus_bits oid context desc openssh name") -_curves = {} - - -p192_names = ["p192", "NIST P-192", "P-192", "prime192v1", "secp192r1", - "nistp192"] - - -def init_p192(): - p = 0xfffffffffffffffffffffffffffffffeffffffffffffffff - b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1 - order = 0xffffffffffffffffffffffff99def836146bc9b1b4d22831 - Gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012 - Gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811 - - p192_modulus = long_to_bytes(p, 24) - p192_b = long_to_bytes(b, 24) - p192_order = long_to_bytes(order, 24) - - ec_p192_context = VoidPointer() - result = _ec_lib.ec_ws_new_context(ec_p192_context.address_of(), - c_uint8_ptr(p192_modulus), - c_uint8_ptr(p192_b), - c_uint8_ptr(p192_order), - c_size_t(len(p192_modulus)), - c_ulonglong(getrandbits(64)) - ) - if result: - raise ImportError("Error %d initializing P-192 context" % result) - - context = SmartPointer(ec_p192_context.get(), _ec_lib.ec_free_context) - p192 = _Curve(Integer(p), - Integer(b), - Integer(order), - Integer(Gx), - Integer(Gy), - None, - 192, - "1.2.840.10045.3.1.1", # ANSI X9.62 / SEC2 - context, - "NIST P-192", - "ecdsa-sha2-nistp192", - "p192") - global p192_names - _curves.update(dict.fromkeys(p192_names, p192)) - - -init_p192() -del init_p192 - - -p224_names = ["p224", "NIST P-224", "P-224", "prime224v1", "secp224r1", - "nistp224"] - - -def init_p224(): - p = 0xffffffffffffffffffffffffffffffff000000000000000000000001 - b = 0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4 - order = 0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d - Gx = 0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21 - Gy = 0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34 - - p224_modulus = long_to_bytes(p, 28) - p224_b = long_to_bytes(b, 28) - p224_order = long_to_bytes(order, 28) - - ec_p224_context = VoidPointer() - result = _ec_lib.ec_ws_new_context(ec_p224_context.address_of(), - c_uint8_ptr(p224_modulus), - c_uint8_ptr(p224_b), - c_uint8_ptr(p224_order), - c_size_t(len(p224_modulus)), - c_ulonglong(getrandbits(64)) - ) - if result: - raise ImportError("Error %d initializing P-224 context" % result) - - context = SmartPointer(ec_p224_context.get(), _ec_lib.ec_free_context) - p224 = _Curve(Integer(p), - Integer(b), - Integer(order), - Integer(Gx), - Integer(Gy), - None, - 224, - "1.3.132.0.33", # SEC 2 - context, - "NIST P-224", - "ecdsa-sha2-nistp224", - "p224") - global p224_names - _curves.update(dict.fromkeys(p224_names, p224)) - - -init_p224() -del init_p224 - - -p256_names = ["p256", "NIST P-256", "P-256", "prime256v1", "secp256r1", - "nistp256"] - - -def init_p256(): - p = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff - b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b - order = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551 - Gx = 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296 - Gy = 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5 - - p256_modulus = long_to_bytes(p, 32) - p256_b = long_to_bytes(b, 32) - p256_order = long_to_bytes(order, 32) - - ec_p256_context = VoidPointer() - result = _ec_lib.ec_ws_new_context(ec_p256_context.address_of(), - c_uint8_ptr(p256_modulus), - c_uint8_ptr(p256_b), - c_uint8_ptr(p256_order), - c_size_t(len(p256_modulus)), - c_ulonglong(getrandbits(64)) - ) - if result: - raise ImportError("Error %d initializing P-256 context" % result) - - context = SmartPointer(ec_p256_context.get(), _ec_lib.ec_free_context) - p256 = _Curve(Integer(p), - Integer(b), - Integer(order), - Integer(Gx), - Integer(Gy), - None, - 256, - "1.2.840.10045.3.1.7", # ANSI X9.62 / SEC2 - context, - "NIST P-256", - "ecdsa-sha2-nistp256", - "p256") - global p256_names - _curves.update(dict.fromkeys(p256_names, p256)) - - -init_p256() -del init_p256 - - -p384_names = ["p384", "NIST P-384", "P-384", "prime384v1", "secp384r1", - "nistp384"] - - -def init_p384(): - p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff - b = 0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef - order = 0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973 - Gx = 0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760aB7 - Gy = 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5F - - p384_modulus = long_to_bytes(p, 48) - p384_b = long_to_bytes(b, 48) - p384_order = long_to_bytes(order, 48) - - ec_p384_context = VoidPointer() - result = _ec_lib.ec_ws_new_context(ec_p384_context.address_of(), - c_uint8_ptr(p384_modulus), - c_uint8_ptr(p384_b), - c_uint8_ptr(p384_order), - c_size_t(len(p384_modulus)), - c_ulonglong(getrandbits(64)) - ) - if result: - raise ImportError("Error %d initializing P-384 context" % result) - - context = SmartPointer(ec_p384_context.get(), _ec_lib.ec_free_context) - p384 = _Curve(Integer(p), - Integer(b), - Integer(order), - Integer(Gx), - Integer(Gy), - None, - 384, - "1.3.132.0.34", # SEC 2 - context, - "NIST P-384", - "ecdsa-sha2-nistp384", - "p384") - global p384_names - _curves.update(dict.fromkeys(p384_names, p384)) - - -init_p384() -del init_p384 - - -p521_names = ["p521", "NIST P-521", "P-521", "prime521v1", "secp521r1", - "nistp521"] - - -def init_p521(): - p = 0x000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - b = 0x00000051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00 - order = 0x000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409 - Gx = 0x000000c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66 - Gy = 0x0000011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650 - - p521_modulus = long_to_bytes(p, 66) - p521_b = long_to_bytes(b, 66) - p521_order = long_to_bytes(order, 66) - - ec_p521_context = VoidPointer() - result = _ec_lib.ec_ws_new_context(ec_p521_context.address_of(), - c_uint8_ptr(p521_modulus), - c_uint8_ptr(p521_b), - c_uint8_ptr(p521_order), - c_size_t(len(p521_modulus)), - c_ulonglong(getrandbits(64)) - ) - if result: - raise ImportError("Error %d initializing P-521 context" % result) - - context = SmartPointer(ec_p521_context.get(), _ec_lib.ec_free_context) - p521 = _Curve(Integer(p), - Integer(b), - Integer(order), - Integer(Gx), - Integer(Gy), - None, - 521, - "1.3.132.0.35", # SEC 2 - context, - "NIST P-521", - "ecdsa-sha2-nistp521", - "p521") - global p521_names - _curves.update(dict.fromkeys(p521_names, p521)) - - -init_p521() -del init_p521 - - -ed25519_names = ["ed25519", "Ed25519"] - - -def init_ed25519(): - p = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed # 2**255 - 19 - order = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed - Gx = 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a - Gy = 0x6666666666666666666666666666666666666666666666666666666666666658 - - ed25519 = _Curve(Integer(p), - None, - Integer(order), - Integer(Gx), - Integer(Gy), - None, - 255, - "1.3.101.112", # RFC8410 - None, - "Ed25519", # Used throughout; do not change - "ssh-ed25519", - "ed25519") - global ed25519_names - _curves.update(dict.fromkeys(ed25519_names, ed25519)) - - -init_ed25519() -del init_ed25519 - - -ed448_names = ["ed448", "Ed448"] - - -def init_ed448(): - p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff # 2**448 - 2**224 - 1 - order = 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3 - Gx = 0x4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e - Gy = 0x693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14 - - ed448_context = VoidPointer() - result = _ed448_lib.ed448_new_context(ed448_context.address_of()) - if result: - raise ImportError("Error %d initializing Ed448 context" % result) - - context = SmartPointer(ed448_context.get(), _ed448_lib.ed448_free_context) - - ed448 = _Curve(Integer(p), - None, - Integer(order), - Integer(Gx), - Integer(Gy), - None, - 448, - "1.3.101.113", # RFC8410 - context, - "Ed448", # Used throughout; do not change - None, - "ed448") - global ed448_names - _curves.update(dict.fromkeys(ed448_names, ed448)) - - -init_ed448() -del init_ed448 - - -class UnsupportedEccFeature(ValueError): - pass - - -class EccPoint(object): - """A class to model a point on an Elliptic Curve. - - The class supports operators for: - - * Adding two points: ``R = S + T`` - * In-place addition: ``S += T`` - * Negating a point: ``R = -T`` - * Comparing two points: ``if S == T: ...`` or ``if S != T: ...`` - * Multiplying a point by a scalar: ``R = S*k`` - * In-place multiplication by a scalar: ``T *= k`` - - :ivar x: The affine X-coordinate of the ECC point - :vartype x: integer - - :ivar y: The affine Y-coordinate of the ECC point - :vartype y: integer - - :ivar xy: The tuple with affine X- and Y- coordinates - """ - - def __init__(self, x, y, curve="p256"): - - try: - self._curve = _curves[curve] - except KeyError: - raise ValueError("Unknown curve name %s" % str(curve)) - self._curve_name = curve - - modulus_bytes = self.size_in_bytes() - - xb = long_to_bytes(x, modulus_bytes) - yb = long_to_bytes(y, modulus_bytes) - if len(xb) != modulus_bytes or len(yb) != modulus_bytes: - raise ValueError("Incorrect coordinate length") - - new_point = lib_func(self, "new_point") - free_func = lib_func(self, "free_point") - - self._point = VoidPointer() - try: - context = self._curve.context.get() - except AttributeError: - context = null_pointer - result = new_point(self._point.address_of(), - c_uint8_ptr(xb), - c_uint8_ptr(yb), - c_size_t(modulus_bytes), - context) - - if result: - if result == 15: - raise ValueError("The EC point does not belong to the curve") - raise ValueError("Error %d while instantiating an EC point" % result) - - # Ensure that object disposal of this Python object will (eventually) - # free the memory allocated by the raw library for the EC point - self._point = SmartPointer(self._point.get(), free_func) - - def set(self, point): - clone = lib_func(self, "clone") - free_func = lib_func(self, "free_point") - - self._point = VoidPointer() - result = clone(self._point.address_of(), - point._point.get()) - - if result: - raise ValueError("Error %d while cloning an EC point" % result) - - self._point = SmartPointer(self._point.get(), free_func) - return self - - def __eq__(self, point): - if not isinstance(point, EccPoint): - return False - - cmp_func = lib_func(self, "cmp") - return 0 == cmp_func(self._point.get(), point._point.get()) - - # Only needed for Python 2 - def __ne__(self, point): - return not self == point - - def __neg__(self): - neg_func = lib_func(self, "neg") - np = self.copy() - result = neg_func(np._point.get()) - if result: - raise ValueError("Error %d while inverting an EC point" % result) - return np - - def copy(self): - """Return a copy of this point.""" - x, y = self.xy - np = EccPoint(x, y, self._curve_name) - return np - - def _is_eddsa(self): - return self._curve.name in ("ed25519", "ed448") - - def is_point_at_infinity(self): - """``True`` if this is the *point-at-infinity*.""" - - if self._is_eddsa(): - return self.x == 0 - else: - return self.xy == (0, 0) - - def point_at_infinity(self): - """Return the *point-at-infinity* for the curve.""" - - if self._is_eddsa(): - return EccPoint(0, 1, self._curve_name) - else: - return EccPoint(0, 0, self._curve_name) - - @property - def x(self): - return self.xy[0] - - @property - def y(self): - return self.xy[1] - - @property - def xy(self): - modulus_bytes = self.size_in_bytes() - xb = bytearray(modulus_bytes) - yb = bytearray(modulus_bytes) - get_xy = lib_func(self, "get_xy") - result = get_xy(c_uint8_ptr(xb), - c_uint8_ptr(yb), - c_size_t(modulus_bytes), - self._point.get()) - if result: - raise ValueError("Error %d while encoding an EC point" % result) - - return (Integer(bytes_to_long(xb)), Integer(bytes_to_long(yb))) - - def size_in_bytes(self): - """Size of each coordinate, in bytes.""" - return (self.size_in_bits() + 7) // 8 - - def size_in_bits(self): - """Size of each coordinate, in bits.""" - return self._curve.modulus_bits - - def double(self): - """Double this point (in-place operation). - - Returns: - This same object (to enable chaining). - """ - - double_func = lib_func(self, "double") - result = double_func(self._point.get()) - if result: - raise ValueError("Error %d while doubling an EC point" % result) - return self - - def __iadd__(self, point): - """Add a second point to this one""" - - add_func = lib_func(self, "add") - result = add_func(self._point.get(), point._point.get()) - if result: - if result == 16: - raise ValueError("EC points are not on the same curve") - raise ValueError("Error %d while adding two EC points" % result) - return self - - def __add__(self, point): - """Return a new point, the addition of this one and another""" - - np = self.copy() - np += point - return np - - def __imul__(self, scalar): - """Multiply this point by a scalar""" - - scalar_func = lib_func(self, "scalar") - if scalar < 0: - raise ValueError("Scalar multiplication is only defined for non-negative integers") - sb = long_to_bytes(scalar) - result = scalar_func(self._point.get(), - c_uint8_ptr(sb), - c_size_t(len(sb)), - c_ulonglong(getrandbits(64))) - if result: - raise ValueError("Error %d during scalar multiplication" % result) - return self - - def __mul__(self, scalar): - """Return a new point, the scalar product of this one""" - - np = self.copy() - np *= scalar - return np - - def __rmul__(self, left_hand): - return self.__mul__(left_hand) - - -# Last piece of initialization -p192_G = EccPoint(_curves['p192'].Gx, _curves['p192'].Gy, "p192") -p192 = _curves['p192']._replace(G=p192_G) -_curves.update(dict.fromkeys(p192_names, p192)) -del p192_G, p192, p192_names - -p224_G = EccPoint(_curves['p224'].Gx, _curves['p224'].Gy, "p224") -p224 = _curves['p224']._replace(G=p224_G) -_curves.update(dict.fromkeys(p224_names, p224)) -del p224_G, p224, p224_names - -p256_G = EccPoint(_curves['p256'].Gx, _curves['p256'].Gy, "p256") -p256 = _curves['p256']._replace(G=p256_G) -_curves.update(dict.fromkeys(p256_names, p256)) -del p256_G, p256, p256_names - -p384_G = EccPoint(_curves['p384'].Gx, _curves['p384'].Gy, "p384") -p384 = _curves['p384']._replace(G=p384_G) -_curves.update(dict.fromkeys(p384_names, p384)) -del p384_G, p384, p384_names - -p521_G = EccPoint(_curves['p521'].Gx, _curves['p521'].Gy, "p521") -p521 = _curves['p521']._replace(G=p521_G) -_curves.update(dict.fromkeys(p521_names, p521)) -del p521_G, p521, p521_names - -ed25519_G = EccPoint(_curves['Ed25519'].Gx, _curves['Ed25519'].Gy, "Ed25519") -ed25519 = _curves['Ed25519']._replace(G=ed25519_G) -_curves.update(dict.fromkeys(ed25519_names, ed25519)) -del ed25519_G, ed25519, ed25519_names - -ed448_G = EccPoint(_curves['Ed448'].Gx, _curves['Ed448'].Gy, "Ed448") -ed448 = _curves['Ed448']._replace(G=ed448_G) -_curves.update(dict.fromkeys(ed448_names, ed448)) -del ed448_G, ed448, ed448_names - - -class EccKey(object): - r"""Class defining an ECC key. - Do not instantiate directly. - Use :func:`generate`, :func:`construct` or :func:`import_key` instead. - - :ivar curve: The name of the curve as defined in the `ECC table`_. - :vartype curve: string - - :ivar pointQ: an ECC point representating the public component. - :vartype pointQ: :class:`EccPoint` - - :ivar d: A scalar that represents the private component - in NIST P curves. It is smaller than the - order of the generator point. - :vartype d: integer - - :ivar seed: A seed that representats the private component - in EdDSA curves - (Ed25519, 32 bytes; Ed448, 57 bytes). - :vartype seed: bytes - """ - - def __init__(self, **kwargs): - """Create a new ECC key - - Keywords: - curve : string - The name of the curve. - d : integer - Mandatory for a private key one NIST P curves. - It must be in the range ``[1..order-1]``. - seed : bytes - Mandatory for a private key on the Ed25519 (32 bytes) - or Ed448 (57 bytes) curve. - point : EccPoint - Mandatory for a public key. If provided for a private key, - the implementation will NOT check whether it matches ``d``. - - Only one parameter among ``d``, ``seed`` or ``point`` may be used. - """ - - kwargs_ = dict(kwargs) - curve_name = kwargs_.pop("curve", None) - self._d = kwargs_.pop("d", None) - self._seed = kwargs_.pop("seed", None) - self._point = kwargs_.pop("point", None) - if curve_name is None and self._point: - curve_name = self._point._curve_name - if kwargs_: - raise TypeError("Unknown parameters: " + str(kwargs_)) - - if curve_name not in _curves: - raise ValueError("Unsupported curve (%s)" % curve_name) - self._curve = _curves[curve_name] - self.curve = self._curve.desc - - count = int(self._d is not None) + int(self._seed is not None) - - if count == 0: - if self._point is None: - raise ValueError("At lest one between parameters 'point', 'd' or 'seed' must be specified") - return - - if count == 2: - raise ValueError("Parameters d and seed are mutually exclusive") - - # NIST P curves work with d, EdDSA works with seed - - if not self._is_eddsa(): - if self._seed is not None: - raise ValueError("Parameter 'seed' can only be used with Ed25519 or Ed448") - self._d = Integer(self._d) - if not 1 <= self._d < self._curve.order: - raise ValueError("Parameter d must be an integer smaller than the curve order") - else: - if self._d is not None: - raise ValueError("Parameter d can only be used with NIST P curves") - # RFC 8032, 5.1.5 - if self._curve.name == "ed25519": - if len(self._seed) != 32: - raise ValueError("Parameter seed must be 32 bytes long for Ed25519") - seed_hash = SHA512.new(self._seed).digest() # h - self._prefix = seed_hash[32:] - tmp = bytearray(seed_hash[:32]) - tmp[0] &= 0xF8 - tmp[31] = (tmp[31] & 0x7F) | 0x40 - # RFC 8032, 5.2.5 - elif self._curve.name == "ed448": - if len(self._seed) != 57: - raise ValueError("Parameter seed must be 57 bytes long for Ed448") - seed_hash = SHAKE256.new(self._seed).read(114) # h - self._prefix = seed_hash[57:] - tmp = bytearray(seed_hash[:57]) - tmp[0] &= 0xFC - tmp[55] |= 0x80 - tmp[56] = 0 - self._d = Integer.from_bytes(tmp, byteorder='little') - - def _is_eddsa(self): - return self._curve.desc in ("Ed25519", "Ed448") - - def __eq__(self, other): - if not isinstance(other, EccKey): - return False - - if other.has_private() != self.has_private(): - return False - - return other.pointQ == self.pointQ - - def __repr__(self): - if self.has_private(): - if self._is_eddsa(): - extra = ", seed=%s" % tostr(binascii.hexlify(self._seed)) - else: - extra = ", d=%d" % int(self._d) - else: - extra = "" - x, y = self.pointQ.xy - return "EccKey(curve='%s', point_x=%d, point_y=%d%s)" % (self._curve.desc, x, y, extra) - - def has_private(self): - """``True`` if this key can be used for making signatures or decrypting data.""" - - return self._d is not None - - # ECDSA - def _sign(self, z, k): - assert 0 < k < self._curve.order - - order = self._curve.order - blind = Integer.random_range(min_inclusive=1, - max_exclusive=order) - - blind_d = self._d * blind - inv_blind_k = (blind * k).inverse(order) - - r = (self._curve.G * k).x % order - s = inv_blind_k * (blind * z + blind_d * r) % order - return (r, s) - - # ECDSA - def _verify(self, z, rs): - order = self._curve.order - sinv = rs[1].inverse(order) - point1 = self._curve.G * ((sinv * z) % order) - point2 = self.pointQ * ((sinv * rs[0]) % order) - return (point1 + point2).x == rs[0] - - @property - def d(self): - if not self.has_private(): - raise ValueError("This is not a private ECC key") - return self._d - - @property - def seed(self): - if not self.has_private(): - raise ValueError("This is not a private ECC key") - return self._seed - - @property - def pointQ(self): - if self._point is None: - self._point = self._curve.G * self._d - return self._point - - def public_key(self): - """A matching ECC public key. - - Returns: - a new :class:`EccKey` object - """ - - return EccKey(curve=self._curve.desc, point=self.pointQ) - - def _export_SEC1(self, compress): - if self._is_eddsa(): - raise ValueError("SEC1 format is unsupported for EdDSA curves") - - # See 2.2 in RFC5480 and 2.3.3 in SEC1 - # - # The first byte is: - # - 0x02: compressed, only X-coordinate, Y-coordinate is even - # - 0x03: compressed, only X-coordinate, Y-coordinate is odd - # - 0x04: uncompressed, X-coordinate is followed by Y-coordinate - # - # PAI is in theory encoded as 0x00. - - modulus_bytes = self.pointQ.size_in_bytes() - - if compress: - if self.pointQ.y.is_odd(): - first_byte = b'\x03' - else: - first_byte = b'\x02' - public_key = (first_byte + - self.pointQ.x.to_bytes(modulus_bytes)) - else: - public_key = (b'\x04' + - self.pointQ.x.to_bytes(modulus_bytes) + - self.pointQ.y.to_bytes(modulus_bytes)) - return public_key - - def _export_eddsa(self): - x, y = self.pointQ.xy - if self._curve.name == "ed25519": - result = bytearray(y.to_bytes(32, byteorder='little')) - result[31] = ((x & 1) << 7) | result[31] - elif self._curve.name == "ed448": - result = bytearray(y.to_bytes(57, byteorder='little')) - result[56] = (x & 1) << 7 - else: - raise ValueError("Not an EdDSA key to export") - return bytes(result) - - def _export_subjectPublicKeyInfo(self, compress): - if self._is_eddsa(): - oid = self._curve.oid - public_key = self._export_eddsa() - params = None - else: - oid = "1.2.840.10045.2.1" # unrestricted - public_key = self._export_SEC1(compress) - params = DerObjectId(self._curve.oid) - - return _create_subject_public_key_info(oid, - public_key, - params) - - def _export_rfc5915_private_der(self, include_ec_params=True): - - assert self.has_private() - - # ECPrivateKey ::= SEQUENCE { - # version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), - # privateKey OCTET STRING, - # parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, - # publicKey [1] BIT STRING OPTIONAL - # } - - # Public key - uncompressed form - modulus_bytes = self.pointQ.size_in_bytes() - public_key = (b'\x04' + - self.pointQ.x.to_bytes(modulus_bytes) + - self.pointQ.y.to_bytes(modulus_bytes)) - - seq = [1, - DerOctetString(self.d.to_bytes(modulus_bytes)), - DerObjectId(self._curve.oid, explicit=0), - DerBitString(public_key, explicit=1)] - - if not include_ec_params: - del seq[2] - - return DerSequence(seq).encode() - - def _export_pkcs8(self, **kwargs): - from Cryptodome.IO import PKCS8 - - if kwargs.get('passphrase', None) is not None and 'protection' not in kwargs: - raise ValueError("At least the 'protection' parameter must be present") - - if self._is_eddsa(): - oid = self._curve.oid - private_key = DerOctetString(self._seed).encode() - params = None - else: - oid = "1.2.840.10045.2.1" # unrestricted - private_key = self._export_rfc5915_private_der(include_ec_params=False) - params = DerObjectId(self._curve.oid) - - result = PKCS8.wrap(private_key, - oid, - key_params=params, - **kwargs) - return result - - def _export_public_pem(self, compress): - from Cryptodome.IO import PEM - - encoded_der = self._export_subjectPublicKeyInfo(compress) - return PEM.encode(encoded_der, "PUBLIC KEY") - - def _export_private_pem(self, passphrase, **kwargs): - from Cryptodome.IO import PEM - - encoded_der = self._export_rfc5915_private_der() - return PEM.encode(encoded_der, "EC PRIVATE KEY", passphrase, **kwargs) - - def _export_private_clear_pkcs8_in_clear_pem(self): - from Cryptodome.IO import PEM - - encoded_der = self._export_pkcs8() - return PEM.encode(encoded_der, "PRIVATE KEY") - - def _export_private_encrypted_pkcs8_in_clear_pem(self, passphrase, **kwargs): - from Cryptodome.IO import PEM - - assert passphrase - if 'protection' not in kwargs: - raise ValueError("At least the 'protection' parameter should be present") - encoded_der = self._export_pkcs8(passphrase=passphrase, **kwargs) - return PEM.encode(encoded_der, "ENCRYPTED PRIVATE KEY") - - def _export_openssh(self, compress): - if self.has_private(): - raise ValueError("Cannot export OpenSSH private keys") - - desc = self._curve.openssh - - if desc is None: - raise ValueError("Cannot export %s keys as OpenSSH" % self._curve.name) - elif desc == "ssh-ed25519": - public_key = self._export_eddsa() - comps = (tobytes(desc), tobytes(public_key)) - else: - modulus_bytes = self.pointQ.size_in_bytes() - - if compress: - first_byte = 2 + self.pointQ.y.is_odd() - public_key = (bchr(first_byte) + - self.pointQ.x.to_bytes(modulus_bytes)) - else: - public_key = (b'\x04' + - self.pointQ.x.to_bytes(modulus_bytes) + - self.pointQ.y.to_bytes(modulus_bytes)) - - middle = desc.split("-")[2] - comps = (tobytes(desc), tobytes(middle), public_key) - - blob = b"".join([struct.pack(">I", len(x)) + x for x in comps]) - return desc + " " + tostr(binascii.b2a_base64(blob)) - - def export_key(self, **kwargs): - """Export this ECC key. - - Args: - format (string): - The output format: - - - ``'DER'``. The key will be encoded in ASN.1 DER format (binary). - For a public key, the ASN.1 ``subjectPublicKeyInfo`` structure - defined in `RFC5480`_ will be used. - For a private key, the ASN.1 ``ECPrivateKey`` structure defined - in `RFC5915`_ is used instead (possibly within a PKCS#8 envelope, - see the ``use_pkcs8`` flag below). - - ``'PEM'``. The key will be encoded in a PEM_ envelope (ASCII). - - ``'OpenSSH'``. The key will be encoded in the OpenSSH_ format - (ASCII, public keys only). - - ``'SEC1'``. The public key (i.e., the EC point) will be encoded - into ``bytes`` according to Section 2.3.3 of `SEC1`_ - (which is a subset of the older X9.62 ITU standard). - Only for NIST P-curves. - - ``'raw'``. The public key will be encoded as ``bytes``, - without any metadata. - - * For NIST P-curves: equivalent to ``'SEC1'``. - * For EdDSA curves: ``bytes`` in the format defined in `RFC8032`_. - - passphrase (bytes or string): - (*Private keys only*) The passphrase to protect the - private key. - - use_pkcs8 (boolean): - (*Private keys only*) - If ``True`` (default and recommended), the `PKCS#8`_ representation - will be used. It must be ``True`` for EdDSA curves. - - If ``False`` and a passphrase is present, the obsolete PEM - encryption will be used. - - protection (string): - When a private key is exported with password-protection - and PKCS#8 (both ``DER`` and ``PEM`` formats), this parameter MUST be - present, - For all possible protection schemes, - refer to :ref:`the encryption parameters of PKCS#8`. - It is recommended to use ``'PBKDF2WithHMAC-SHA5126AndAES128-CBC'``. - - compress (boolean): - If ``True``, the method returns a more compact representation - of the public key, with the X-coordinate only. - - If ``False`` (default), the method returns the full public key. - - This parameter is ignored for EdDSA curves, as compression is - mandatory. - - prot_params (dict): - When a private key is exported with password-protection - and PKCS#8 (both ``DER`` and ``PEM`` formats), this dictionary - contains the parameters to use to derive the encryption key - from the passphrase. - For all possible values, - refer to :ref:`the encryption parameters of PKCS#8`. - The recommendation is to use ``{'iteration_count':21000}`` for PBKDF2, - and ``{'iteration_count':131072}`` for scrypt. - - .. warning:: - If you don't provide a passphrase, the private key will be - exported in the clear! - - .. note:: - When exporting a private key with password-protection and `PKCS#8`_ - (both ``DER`` and ``PEM`` formats), any extra parameters - to ``export_key()`` will be passed to :mod:`Cryptodome.IO.PKCS8`. - - .. _PEM: http://www.ietf.org/rfc/rfc1421.txt - .. _`PEM encryption`: http://www.ietf.org/rfc/rfc1423.txt - .. _OpenSSH: http://www.openssh.com/txt/rfc5656.txt - .. _RFC5480: https://tools.ietf.org/html/rfc5480 - .. _SEC1: https://www.secg.org/sec1-v2.pdf - - Returns: - A multi-line string (for ``'PEM'`` and ``'OpenSSH'``) or - ``bytes`` (for ``'DER'``, ``'SEC1'``, and ``'raw'``) with the encoded key. - """ - - args = kwargs.copy() - ext_format = args.pop("format") - if ext_format not in ("PEM", "DER", "OpenSSH", "SEC1", "raw"): - raise ValueError("Unknown format '%s'" % ext_format) - - compress = args.pop("compress", False) - - if self.has_private(): - passphrase = args.pop("passphrase", None) - if is_string(passphrase): - passphrase = tobytes(passphrase) - if not passphrase: - raise ValueError("Empty passphrase") - use_pkcs8 = args.pop("use_pkcs8", True) - - if not use_pkcs8: - if self._is_eddsa(): - raise ValueError("'pkcs8' must be True for EdDSA curves") - if 'protection' in args: - raise ValueError("'protection' is only supported for PKCS#8") - - if ext_format == "PEM": - if use_pkcs8: - if passphrase: - return self._export_private_encrypted_pkcs8_in_clear_pem(passphrase, **args) - else: - return self._export_private_clear_pkcs8_in_clear_pem() - else: - return self._export_private_pem(passphrase, **args) - elif ext_format == "DER": - # DER - if passphrase and not use_pkcs8: - raise ValueError("Private keys can only be encrpyted with DER using PKCS#8") - if use_pkcs8: - return self._export_pkcs8(passphrase=passphrase, **args) - else: - return self._export_rfc5915_private_der() - else: - raise ValueError("Private keys cannot be exported " - "in the '%s' format" % ext_format) - else: # Public key - if args: - raise ValueError("Unexpected parameters: '%s'" % args) - if ext_format == "PEM": - return self._export_public_pem(compress) - elif ext_format == "DER": - return self._export_subjectPublicKeyInfo(compress) - elif ext_format == "SEC1": - return self._export_SEC1(compress) - elif ext_format == "raw": - if self._curve.name in ('ed25519', 'ed448'): - return self._export_eddsa() - else: - return self._export_SEC1(compress) - else: - return self._export_openssh(compress) - - -def generate(**kwargs): - """Generate a new private key on the given curve. - - Args: - - curve (string): - Mandatory. It must be a curve name defined in the `ECC table`_. - - randfunc (callable): - Optional. The RNG to read randomness from. - If ``None``, :func:`Cryptodome.Random.get_random_bytes` is used. - """ - - curve_name = kwargs.pop("curve") - curve = _curves[curve_name] - randfunc = kwargs.pop("randfunc", get_random_bytes) - if kwargs: - raise TypeError("Unknown parameters: " + str(kwargs)) - - if _curves[curve_name].name == "ed25519": - seed = randfunc(32) - new_key = EccKey(curve=curve_name, seed=seed) - elif _curves[curve_name].name == "ed448": - seed = randfunc(57) - new_key = EccKey(curve=curve_name, seed=seed) - else: - d = Integer.random_range(min_inclusive=1, - max_exclusive=curve.order, - randfunc=randfunc) - new_key = EccKey(curve=curve_name, d=d) - - return new_key - - -def construct(**kwargs): - """Build a new ECC key (private or public) starting - from some base components. - - In most cases, you will already have an existing key - which you can read in with :func:`import_key` instead - of this function. - - Args: - curve (string): - Mandatory. The name of the elliptic curve, as defined in the `ECC table`_. - - d (integer): - Mandatory for a private key and a NIST P-curve (e.g., P-256): - the integer in the range ``[1..order-1]`` that represents the key. - - seed (bytes): - Mandatory for a private key and an EdDSA curve. - It must be 32 bytes for Ed25519, and 57 bytes for Ed448. - - point_x (integer): - Mandatory for a public key: the X coordinate (affine) of the ECC point. - - point_y (integer): - Mandatory for a public key: the Y coordinate (affine) of the ECC point. - - Returns: - :class:`EccKey` : a new ECC key object - """ - - curve_name = kwargs["curve"] - curve = _curves[curve_name] - point_x = kwargs.pop("point_x", None) - point_y = kwargs.pop("point_y", None) - - if "point" in kwargs: - raise TypeError("Unknown keyword: point") - - if None not in (point_x, point_y): - # ValueError is raised if the point is not on the curve - kwargs["point"] = EccPoint(point_x, point_y, curve_name) - - new_key = EccKey(**kwargs) - - # Validate that the private key matches the public one - # because EccKey will not do that automatically - if new_key.has_private() and 'point' in kwargs: - pub_key = curve.G * new_key.d - if pub_key.xy != (point_x, point_y): - raise ValueError("Private and public ECC keys do not match") - - return new_key - - -def _import_public_der(ec_point, curve_oid=None, curve_name=None): - """Convert an encoded EC point into an EccKey object - - ec_point: byte string with the EC point (SEC1-encoded) - curve_oid: string with the name the curve - curve_name: string with the OID of the curve - - Either curve_id or curve_name must be specified - - """ - - for _curve_name, curve in _curves.items(): - if curve_oid and curve.oid == curve_oid: - break - if curve_name == _curve_name: - break - else: - if curve_oid: - raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_oid) - else: - raise UnsupportedEccFeature("Unsupported ECC curve (%s)" % curve_name) - - # See 2.2 in RFC5480 and 2.3.3 in SEC1 - # The first byte is: - # - 0x02: compressed, only X-coordinate, Y-coordinate is even - # - 0x03: compressed, only X-coordinate, Y-coordinate is odd - # - 0x04: uncompressed, X-coordinate is followed by Y-coordinate - # - # PAI is in theory encoded as 0x00. - - modulus_bytes = curve.p.size_in_bytes() - point_type = bord(ec_point[0]) - - # Uncompressed point - if point_type == 0x04: - if len(ec_point) != (1 + 2 * modulus_bytes): - raise ValueError("Incorrect EC point length") - x = Integer.from_bytes(ec_point[1:modulus_bytes+1]) - y = Integer.from_bytes(ec_point[modulus_bytes+1:]) - # Compressed point - elif point_type in (0x02, 0x03): - if len(ec_point) != (1 + modulus_bytes): - raise ValueError("Incorrect EC point length") - x = Integer.from_bytes(ec_point[1:]) - # Right now, we only support Short Weierstrass curves - y = (x**3 - x*3 + curve.b).sqrt(curve.p) - if point_type == 0x02 and y.is_odd(): - y = curve.p - y - if point_type == 0x03 and y.is_even(): - y = curve.p - y - else: - raise ValueError("Incorrect EC point encoding") - - return construct(curve=_curve_name, point_x=x, point_y=y) - - -def _import_subjectPublicKeyInfo(encoded, *kwargs): - """Convert a subjectPublicKeyInfo into an EccKey object""" - - # See RFC5480 - - # Parse the generic subjectPublicKeyInfo structure - oid, ec_point, params = _expand_subject_public_key_info(encoded) - - nist_p_oids = ( - "1.2.840.10045.2.1", # id-ecPublicKey (unrestricted) - "1.3.132.1.12", # id-ecDH - "1.3.132.1.13" # id-ecMQV - ) - eddsa_oids = { - "1.3.101.112": ("Ed25519", _import_ed25519_public_key), # id-Ed25519 - "1.3.101.113": ("Ed448", _import_ed448_public_key) # id-Ed448 - } - - if oid in nist_p_oids: - # See RFC5480 - - # Parameters are mandatory and encoded as ECParameters - # ECParameters ::= CHOICE { - # namedCurve OBJECT IDENTIFIER - # -- implicitCurve NULL - # -- specifiedCurve SpecifiedECDomain - # } - # implicitCurve and specifiedCurve are not supported (as per RFC) - if not params: - raise ValueError("Missing ECC parameters for ECC OID %s" % oid) - try: - curve_oid = DerObjectId().decode(params).value - except ValueError: - raise ValueError("Error decoding namedCurve") - - # ECPoint ::= OCTET STRING - return _import_public_der(ec_point, curve_oid=curve_oid) - - elif oid in eddsa_oids: - # See RFC8410 - curve_name, import_eddsa_public_key = eddsa_oids[oid] - - # Parameters must be absent - if params: - raise ValueError("Unexpected ECC parameters for ECC OID %s" % oid) - - x, y = import_eddsa_public_key(ec_point) - return construct(point_x=x, point_y=y, curve=curve_name) - else: - raise UnsupportedEccFeature("Unsupported ECC OID: %s" % oid) - - -def _import_rfc5915_der(encoded, passphrase, curve_oid=None): - - # See RFC5915 https://tools.ietf.org/html/rfc5915 - # - # ECPrivateKey ::= SEQUENCE { - # version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), - # privateKey OCTET STRING, - # parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, - # publicKey [1] BIT STRING OPTIONAL - # } - - private_key = DerSequence().decode(encoded, nr_elements=(3, 4)) - if private_key[0] != 1: - raise ValueError("Incorrect ECC private key version") - - try: - parameters = DerObjectId(explicit=0).decode(private_key[2]).value - if curve_oid is not None and parameters != curve_oid: - raise ValueError("Curve mismatch") - curve_oid = parameters - except ValueError: - pass - - if curve_oid is None: - raise ValueError("No curve found") - - for curve_name, curve in _curves.items(): - if curve.oid == curve_oid: - break - else: - raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_oid) - - scalar_bytes = DerOctetString().decode(private_key[1]).payload - modulus_bytes = curve.p.size_in_bytes() - if len(scalar_bytes) != modulus_bytes: - raise ValueError("Private key is too small") - d = Integer.from_bytes(scalar_bytes) - - # Decode public key (if any) - if len(private_key) > 2: - public_key_enc = DerBitString(explicit=1).decode(private_key[-1]).value - public_key = _import_public_der(public_key_enc, curve_oid=curve_oid) - point_x = public_key.pointQ.x - point_y = public_key.pointQ.y - else: - point_x = point_y = None - - return construct(curve=curve_name, d=d, point_x=point_x, point_y=point_y) - - -def _import_pkcs8(encoded, passphrase): - from Cryptodome.IO import PKCS8 - - algo_oid, private_key, params = PKCS8.unwrap(encoded, passphrase) - - nist_p_oids = ( - "1.2.840.10045.2.1", # id-ecPublicKey (unrestricted) - "1.3.132.1.12", # id-ecDH - "1.3.132.1.13" # id-ecMQV - ) - eddsa_oids = { - "1.3.101.112": "Ed25519", # id-Ed25519 - "1.3.101.113": "Ed448", # id-Ed448 - } - - if algo_oid in nist_p_oids: - curve_oid = DerObjectId().decode(params).value - return _import_rfc5915_der(private_key, passphrase, curve_oid) - elif algo_oid in eddsa_oids: - if params is not None: - raise ValueError("EdDSA ECC private key must not have parameters") - curve_oid = None - seed = DerOctetString().decode(private_key).payload - return construct(curve=eddsa_oids[algo_oid], seed=seed) - else: - raise UnsupportedEccFeature("Unsupported ECC purpose (OID: %s)" % algo_oid) - - -def _import_x509_cert(encoded, *kwargs): - - sp_info = _extract_subject_public_key_info(encoded) - return _import_subjectPublicKeyInfo(sp_info) - - -def _import_der(encoded, passphrase): - - try: - return _import_subjectPublicKeyInfo(encoded, passphrase) - except UnsupportedEccFeature as err: - raise err - except (ValueError, TypeError, IndexError): - pass - - try: - return _import_x509_cert(encoded, passphrase) - except UnsupportedEccFeature as err: - raise err - except (ValueError, TypeError, IndexError): - pass - - try: - return _import_rfc5915_der(encoded, passphrase) - except UnsupportedEccFeature as err: - raise err - except (ValueError, TypeError, IndexError): - pass - - try: - return _import_pkcs8(encoded, passphrase) - except UnsupportedEccFeature as err: - raise err - except (ValueError, TypeError, IndexError): - pass - - raise ValueError("Not an ECC DER key") - - -def _import_openssh_public(encoded): - parts = encoded.split(b' ') - if len(parts) not in (2, 3): - raise ValueError("Not an openssh public key") - - try: - keystring = binascii.a2b_base64(parts[1]) - - keyparts = [] - while len(keystring) > 4: - lk = struct.unpack(">I", keystring[:4])[0] - keyparts.append(keystring[4:4 + lk]) - keystring = keystring[4 + lk:] - - if parts[0] != keyparts[0]: - raise ValueError("Mismatch in openssh public key") - - # NIST P curves - if parts[0].startswith(b"ecdsa-sha2-"): - - for curve_name, curve in _curves.items(): - if curve.openssh is None: - continue - if not curve.openssh.startswith("ecdsa-sha2"): - continue - middle = tobytes(curve.openssh.split("-")[2]) - if keyparts[1] == middle: - break - else: - raise ValueError("Unsupported ECC curve: " + middle) - - ecc_key = _import_public_der(keyparts[2], curve_oid=curve.oid) - - # EdDSA - elif parts[0] == b"ssh-ed25519": - x, y = _import_ed25519_public_key(keyparts[1]) - ecc_key = construct(curve="Ed25519", point_x=x, point_y=y) - else: - raise ValueError("Unsupported SSH key type: " + parts[0]) - - except (IndexError, TypeError, binascii.Error): - raise ValueError("Error parsing SSH key type: " + parts[0]) - - return ecc_key - - -def _import_openssh_private_ecc(data, password): - - from ._openssh import (import_openssh_private_generic, - read_bytes, read_string, check_padding) - - key_type, decrypted = import_openssh_private_generic(data, password) - - eddsa_keys = { - "ssh-ed25519": ("Ed25519", _import_ed25519_public_key, 32), - } - - # https://datatracker.ietf.org/doc/html/draft-miller-ssh-agent-04 - if key_type.startswith("ecdsa-sha2"): - - ecdsa_curve_name, decrypted = read_string(decrypted) - if ecdsa_curve_name not in _curves: - raise UnsupportedEccFeature("Unsupported ECC curve %s" % ecdsa_curve_name) - curve = _curves[ecdsa_curve_name] - modulus_bytes = (curve.modulus_bits + 7) // 8 - - public_key, decrypted = read_bytes(decrypted) - - if bord(public_key[0]) != 4: - raise ValueError("Only uncompressed OpenSSH EC keys are supported") - if len(public_key) != 2 * modulus_bytes + 1: - raise ValueError("Incorrect public key length") - - point_x = Integer.from_bytes(public_key[1:1+modulus_bytes]) - point_y = Integer.from_bytes(public_key[1+modulus_bytes:]) - - private_key, decrypted = read_bytes(decrypted) - d = Integer.from_bytes(private_key) - - params = {'d': d, 'curve': ecdsa_curve_name} - - elif key_type in eddsa_keys: - - curve_name, import_eddsa_public_key, seed_len = eddsa_keys[key_type] - - public_key, decrypted = read_bytes(decrypted) - point_x, point_y = import_eddsa_public_key(public_key) - - private_public_key, decrypted = read_bytes(decrypted) - seed = private_public_key[:seed_len] - - params = {'seed': seed, 'curve': curve_name} - else: - raise ValueError("Unsupport SSH agent key type:" + key_type) - - _, padded = read_string(decrypted) # Comment - check_padding(padded) - - return construct(point_x=point_x, point_y=point_y, **params) - - -def _import_ed25519_public_key(encoded): - """Import an Ed25519 ECC public key, encoded as raw bytes as described - in RFC8032_. - - Args: - encoded (bytes): - The Ed25519 public key to import. It must be 32 bytes long. - - Returns: - :class:`EccKey` : a new ECC key object - - Raises: - ValueError: when the given key cannot be parsed. - - .. _RFC8032: https://datatracker.ietf.org/doc/html/rfc8032 - """ - - if len(encoded) != 32: - raise ValueError("Incorrect length. Only Ed25519 public keys are supported.") - - p = Integer(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed) # 2**255 - 19 - d = 37095705934669439343138083508754565189542113879843219016388785533085940283555 - - y = bytearray(encoded) - x_lsb = y[31] >> 7 - y[31] &= 0x7F - point_y = Integer.from_bytes(y, byteorder='little') - if point_y >= p: - raise ValueError("Invalid Ed25519 key (y)") - if point_y == 1: - return 0, 1 - - u = (point_y**2 - 1) % p - v = ((point_y**2 % p) * d + 1) % p - try: - v_inv = v.inverse(p) - x2 = (u * v_inv) % p - point_x = Integer._tonelli_shanks(x2, p) - if (point_x & 1) != x_lsb: - point_x = p - point_x - except ValueError: - raise ValueError("Invalid Ed25519 public key") - return point_x, point_y - - -def _import_ed448_public_key(encoded): - """Import an Ed448 ECC public key, encoded as raw bytes as described - in RFC8032_. - - Args: - encoded (bytes): - The Ed448 public key to import. It must be 57 bytes long. - - Returns: - :class:`EccKey` : a new ECC key object - - Raises: - ValueError: when the given key cannot be parsed. - - .. _RFC8032: https://datatracker.ietf.org/doc/html/rfc8032 - """ - - if len(encoded) != 57: - raise ValueError("Incorrect length. Only Ed448 public keys are supported.") - - p = Integer(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff) # 2**448 - 2**224 - 1 - d = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffff6756 - - y = encoded[:56] - x_lsb = bord(encoded[56]) >> 7 - point_y = Integer.from_bytes(y, byteorder='little') - if point_y >= p: - raise ValueError("Invalid Ed448 key (y)") - if point_y == 1: - return 0, 1 - - u = (point_y**2 - 1) % p - v = ((point_y**2 % p) * d - 1) % p - try: - v_inv = v.inverse(p) - x2 = (u * v_inv) % p - point_x = Integer._tonelli_shanks(x2, p) - if (point_x & 1) != x_lsb: - point_x = p - point_x - except ValueError: - raise ValueError("Invalid Ed448 public key") - return point_x, point_y - - -def import_key(encoded, passphrase=None, curve_name=None): - """Import an ECC key (public or private). - - Args: - encoded (bytes or multi-line string): - The ECC key to import. - The function will try to automatically detect the right format. - - Supported formats for an ECC **public** key: - - * X.509 certificate: binary (DER) or ASCII (PEM). - * X.509 ``subjectPublicKeyInfo``: binary (DER) or ASCII (PEM). - * SEC1_ (or X9.62), as ``bytes``. NIST P curves only. - You must also provide the ``curve_name`` (with a value from the `ECC table`_) - * OpenSSH line, defined in RFC5656_ and RFC8709_ (ASCII). - This is normally the content of files like ``~/.ssh/id_ecdsa.pub``. - - Supported formats for an ECC **private** key: - - * A binary ``ECPrivateKey`` structure, as defined in `RFC5915`_ (DER). - NIST P curves only. - * A `PKCS#8`_ structure (or the more recent Asymmetric Key Package, RFC5958_): binary (DER) or ASCII (PEM). - * `OpenSSH 6.5`_ and newer versions (ASCII). - - Private keys can be in the clear or password-protected. - - For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. - - passphrase (byte string): - The passphrase to use for decrypting a private key. - Encryption may be applied protected at the PEM level (not recommended) - or at the PKCS#8 level (recommended). - This parameter is ignored if the key in input is not encrypted. - - curve_name (string): - For a SEC1 encoding only. This is the name of the curve, - as defined in the `ECC table`_. - - .. note:: - - To import EdDSA private and public keys, when encoded as raw ``bytes``, use: - - * :func:`Cryptodome.Signature.eddsa.import_public_key`, or - * :func:`Cryptodome.Signature.eddsa.import_private_key`. - - Returns: - :class:`EccKey` : a new ECC key object - - Raises: - ValueError: when the given key cannot be parsed (possibly because - the pass phrase is wrong). - - .. _RFC1421: https://datatracker.ietf.org/doc/html/rfc1421 - .. _RFC1423: https://datatracker.ietf.org/doc/html/rfc1423 - .. _RFC5915: https://datatracker.ietf.org/doc/html/rfc5915 - .. _RFC5656: https://datatracker.ietf.org/doc/html/rfc5656 - .. _RFC8709: https://datatracker.ietf.org/doc/html/rfc8709 - .. _RFC5958: https://datatracker.ietf.org/doc/html/rfc5958 - .. _`PKCS#8`: https://datatracker.ietf.org/doc/html/rfc5208 - .. _`OpenSSH 6.5`: https://flak.tedunangst.com/post/new-openssh-key-format-and-bcrypt-pbkdf - .. _SEC1: https://www.secg.org/sec1-v2.pdf - """ - - from Cryptodome.IO import PEM - - encoded = tobytes(encoded) - if passphrase is not None: - passphrase = tobytes(passphrase) - - # PEM - if encoded.startswith(b'-----BEGIN OPENSSH PRIVATE KEY'): - text_encoded = tostr(encoded) - openssh_encoded, marker, enc_flag = PEM.decode(text_encoded, passphrase) - result = _import_openssh_private_ecc(openssh_encoded, passphrase) - return result - - elif encoded.startswith(b'-----'): - - text_encoded = tostr(encoded) - - # Remove any EC PARAMETERS section - # Ignore its content because the curve type must be already given in the key - ecparams_start = "-----BEGIN EC PARAMETERS-----" - ecparams_end = "-----END EC PARAMETERS-----" - text_encoded = re.sub(ecparams_start + ".*?" + ecparams_end, "", - text_encoded, - flags=re.DOTALL) - - der_encoded, marker, enc_flag = PEM.decode(text_encoded, passphrase) - if enc_flag: - passphrase = None - try: - result = _import_der(der_encoded, passphrase) - except UnsupportedEccFeature as uef: - raise uef - except ValueError: - raise ValueError("Invalid DER encoding inside the PEM file") - return result - - # OpenSSH - if encoded.startswith((b'ecdsa-sha2-', b'ssh-ed25519')): - return _import_openssh_public(encoded) - - # DER - if len(encoded) > 0 and bord(encoded[0]) == 0x30: - return _import_der(encoded, passphrase) - - # SEC1 - if len(encoded) > 0 and bord(encoded[0]) in (0x02, 0x03, 0x04): - if curve_name is None: - raise ValueError("No curve name was provided") - return _import_public_der(encoded, curve_name=curve_name) - - raise ValueError("ECC key format is not supported") - - -if __name__ == "__main__": - - import time - - d = 0xc51e4753afdec1e6b6c6a5b992f43f8dd0c7a8933072708b6522468b2ffb06fd - - point = _curves['p256'].G.copy() - count = 3000 - - start = time.time() - for x in range(count): - pointX = point * d - print("(P-256 G)", (time.time() - start) / count * 1000, "ms") - - start = time.time() - for x in range(count): - pointX = pointX * d - print("(P-256 arbitrary point)", (time.time() - start) / count * 1000, "ms") diff --git a/resources/lib/deps/Cryptodome/PublicKey/ECC.pyi b/resources/lib/deps/Cryptodome/PublicKey/ECC.pyi deleted file mode 100644 index e3c4ed5..0000000 --- a/resources/lib/deps/Cryptodome/PublicKey/ECC.pyi +++ /dev/null @@ -1,102 +0,0 @@ -from __future__ import annotations - -from typing import Union, Callable, Optional, Tuple, Dict, NamedTuple, Any, overload, Literal -from typing_extensions import TypedDict, Unpack, NotRequired - -from Cryptodome.Math.Numbers import Integer -from Cryptodome.IO._PBES import ProtParams - -RNG = Callable[[int], bytes] - - -class UnsupportedEccFeature(ValueError): - ... - - -class EccPoint(object): - def __init__(self, - x: Union[int, Integer], - y: Union[int, Integer], - curve: Optional[str] = ...) -> None: ... - - def set(self, point: EccPoint) -> EccPoint: ... - def __eq__(self, point: object) -> bool: ... - def __neg__(self) -> EccPoint: ... - def copy(self) -> EccPoint: ... - def is_point_at_infinity(self) -> bool: ... - def point_at_infinity(self) -> EccPoint: ... - @property - def x(self) -> int: ... - @property - def y(self) -> int: ... - @property - def xy(self) -> Tuple[int, int]: ... - def size_in_bytes(self) -> int: ... - def size_in_bits(self) -> int: ... - def double(self) -> EccPoint: ... - def __iadd__(self, point: EccPoint) -> EccPoint: ... - def __add__(self, point: EccPoint) -> EccPoint: ... - def __imul__(self, scalar: int) -> EccPoint: ... - def __mul__(self, scalar: int) -> EccPoint: ... - - -class ExportParams(TypedDict): - passphrase: NotRequired[Union[bytes, str]] - use_pkcs8: NotRequired[bool] - protection: NotRequired[str] - compress: NotRequired[bool] - prot_params: NotRequired[ProtParams] - - -class EccKey(object): - curve: str - def __init__(self, *, curve: str = ..., d: int = ..., point: EccPoint = ...) -> None: ... - def __eq__(self, other: object) -> bool: ... - def __repr__(self) -> str: ... - def has_private(self) -> bool: ... - @property - def d(self) -> int: ... - @property - def pointQ(self) -> EccPoint: ... - def public_key(self) -> EccKey: ... - - @overload - def export_key(self, - *, - format: Literal['PEM', 'OpenSSH'], - **kwargs: Unpack[ExportParams]) -> str: ... - - @overload - def export_key(self, - *, - format: Literal['DER', 'SEC1', 'raw'], - **kwargs: Unpack[ExportParams]) -> bytes: ... - - -_Curve = NamedTuple("_Curve", [('p', Integer), - ('order', Integer), - ('b', Integer), - ('Gx', Integer), - ('Gy', Integer), - ('G', EccPoint), - ('modulus_bits', int), - ('oid', str), - ('context', Any), - ('desc', str), - ('openssh', Union[str, None]), - ]) - -_curves: Dict[str, _Curve] - - -def generate(**kwargs: Union[str, RNG]) -> EccKey: ... -def construct(**kwargs: Union[str, int]) -> EccKey: ... - - -def import_key(encoded: Union[bytes, str], - passphrase: Optional[str] = None, - curve_name: Optional[str] = None) -> EccKey: ... - - -def _import_ed25519_public_key(encoded: bytes) -> EccKey: ... -def _import_ed448_public_key(encoded: bytes) -> EccKey: ... diff --git a/resources/lib/deps/Cryptodome/PublicKey/ElGamal.py b/resources/lib/deps/Cryptodome/PublicKey/ElGamal.py deleted file mode 100644 index 95c219e..0000000 --- a/resources/lib/deps/Cryptodome/PublicKey/ElGamal.py +++ /dev/null @@ -1,286 +0,0 @@ -# -# ElGamal.py : ElGamal encryption/decryption and signatures -# -# Part of the Python Cryptography Toolkit -# -# Originally written by: A.M. Kuchling -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -__all__ = ['generate', 'construct', 'ElGamalKey'] - -from Cryptodome import Random -from Cryptodome.Math.Primality import ( generate_probable_safe_prime, - test_probable_prime, COMPOSITE ) -from Cryptodome.Math.Numbers import Integer - -# Generate an ElGamal key with N bits -def generate(bits, randfunc): - """Randomly generate a fresh, new ElGamal key. - - The key will be safe for use for both encryption and signature - (although it should be used for **only one** purpose). - - Args: - bits (int): - Key length, or size (in bits) of the modulus *p*. - The recommended value is 2048. - randfunc (callable): - Random number generation function; it should accept - a single integer *N* and return a string of random - *N* random bytes. - - Return: - an :class:`ElGamalKey` object - """ - - obj=ElGamalKey() - - # Generate a safe prime p - # See Algorithm 4.86 in Handbook of Applied Cryptography - obj.p = generate_probable_safe_prime(exact_bits=bits, randfunc=randfunc) - q = (obj.p - 1) >> 1 - - # Generate generator g - while 1: - # Choose a square residue; it will generate a cyclic group of order q. - obj.g = pow(Integer.random_range(min_inclusive=2, - max_exclusive=obj.p, - randfunc=randfunc), 2, obj.p) - - # We must avoid g=2 because of Bleichenbacher's attack described - # in "Generating ElGamal signatures without knowning the secret key", - # 1996 - if obj.g in (1, 2): - continue - - # Discard g if it divides p-1 because of the attack described - # in Note 11.67 (iii) in HAC - if (obj.p - 1) % obj.g == 0: - continue - - # g^{-1} must not divide p-1 because of Khadir's attack - # described in "Conditions of the generator for forging ElGamal - # signature", 2011 - ginv = obj.g.inverse(obj.p) - if (obj.p - 1) % ginv == 0: - continue - - # Found - break - - # Generate private key x - obj.x = Integer.random_range(min_inclusive=2, - max_exclusive=obj.p-1, - randfunc=randfunc) - # Generate public key y - obj.y = pow(obj.g, obj.x, obj.p) - return obj - -def construct(tup): - r"""Construct an ElGamal key from a tuple of valid ElGamal components. - - The modulus *p* must be a prime. - The following conditions must apply: - - .. math:: - - \begin{align} - &1 < g < p-1 \\ - &g^{p-1} = 1 \text{ mod } 1 \\ - &1 < x < p-1 \\ - &g^x = y \text{ mod } p - \end{align} - - Args: - tup (tuple): - A tuple with either 3 or 4 integers, - in the following order: - - 1. Modulus (*p*). - 2. Generator (*g*). - 3. Public key (*y*). - 4. Private key (*x*). Optional. - - Raises: - ValueError: when the key being imported fails the most basic ElGamal validity checks. - - Returns: - an :class:`ElGamalKey` object - """ - - obj=ElGamalKey() - if len(tup) not in [3,4]: - raise ValueError('argument for construct() wrong length') - for i in range(len(tup)): - field = obj._keydata[i] - setattr(obj, field, Integer(tup[i])) - - fmt_error = test_probable_prime(obj.p) == COMPOSITE - fmt_error |= obj.g<=1 or obj.g>=obj.p - fmt_error |= pow(obj.g, obj.p-1, obj.p)!=1 - fmt_error |= obj.y<1 or obj.y>=obj.p - if len(tup)==4: - fmt_error |= obj.x<=1 or obj.x>=obj.p - fmt_error |= pow(obj.g, obj.x, obj.p)!=obj.y - - if fmt_error: - raise ValueError("Invalid ElGamal key components") - - return obj - -class ElGamalKey(object): - r"""Class defining an ElGamal key. - Do not instantiate directly. - Use :func:`generate` or :func:`construct` instead. - - :ivar p: Modulus - :vartype d: integer - - :ivar g: Generator - :vartype e: integer - - :ivar y: Public key component - :vartype y: integer - - :ivar x: Private key component - :vartype x: integer - """ - - #: Dictionary of ElGamal parameters. - #: - #: A public key will only have the following entries: - #: - #: - **y**, the public key. - #: - **g**, the generator. - #: - **p**, the modulus. - #: - #: A private key will also have: - #: - #: - **x**, the private key. - _keydata=['p', 'g', 'y', 'x'] - - def __init__(self, randfunc=None): - if randfunc is None: - randfunc = Random.new().read - self._randfunc = randfunc - - def _encrypt(self, M, K): - a=pow(self.g, K, self.p) - b=( pow(self.y, K, self.p)*M ) % self.p - return [int(a), int(b)] - - def _decrypt(self, M): - if (not hasattr(self, 'x')): - raise TypeError('Private key not available in this object') - r = Integer.random_range(min_inclusive=2, - max_exclusive=self.p-1, - randfunc=self._randfunc) - a_blind = (pow(self.g, r, self.p) * M[0]) % self.p - ax=pow(a_blind, self.x, self.p) - plaintext_blind = (ax.inverse(self.p) * M[1] ) % self.p - plaintext = (plaintext_blind * pow(self.y, r, self.p)) % self.p - return int(plaintext) - - def _sign(self, M, K): - if (not hasattr(self, 'x')): - raise TypeError('Private key not available in this object') - p1=self.p-1 - K = Integer(K) - if (K.gcd(p1)!=1): - raise ValueError('Bad K value: GCD(K,p-1)!=1') - a=pow(self.g, K, self.p) - t=(Integer(M)-self.x*a) % p1 - while t<0: t=t+p1 - b=(t*K.inverse(p1)) % p1 - return [int(a), int(b)] - - def _verify(self, M, sig): - sig = [Integer(x) for x in sig] - if sig[0]<1 or sig[0]>self.p-1: - return 0 - v1=pow(self.y, sig[0], self.p) - v1=(v1*pow(sig[0], sig[1], self.p)) % self.p - v2=pow(self.g, M, self.p) - if v1==v2: - return 1 - return 0 - - def has_private(self): - """Whether this is an ElGamal private key""" - - if hasattr(self, 'x'): - return 1 - else: - return 0 - - def can_encrypt(self): - return True - - def can_sign(self): - return True - - def publickey(self): - """A matching ElGamal public key. - - Returns: - a new :class:`ElGamalKey` object - """ - return construct((self.p, self.g, self.y)) - - def __eq__(self, other): - if bool(self.has_private()) != bool(other.has_private()): - return False - - result = True - for comp in self._keydata: - result = result and (getattr(self.key, comp, None) == - getattr(other.key, comp, None)) - return result - - def __ne__(self, other): - return not self.__eq__(other) - - def __getstate__(self): - # ElGamal key is not pickable - from pickle import PicklingError - raise PicklingError - - # Methods defined in PyCryptodome that we don't support anymore - - def sign(self, M, K): - raise NotImplementedError - - def verify(self, M, signature): - raise NotImplementedError - - def encrypt(self, plaintext, K): - raise NotImplementedError - - def decrypt(self, ciphertext): - raise NotImplementedError - - def blind(self, M, B): - raise NotImplementedError - - def unblind(self, M, B): - raise NotImplementedError - - def size(self): - raise NotImplementedError diff --git a/resources/lib/deps/Cryptodome/PublicKey/ElGamal.pyi b/resources/lib/deps/Cryptodome/PublicKey/ElGamal.pyi deleted file mode 100644 index 9048531..0000000 --- a/resources/lib/deps/Cryptodome/PublicKey/ElGamal.pyi +++ /dev/null @@ -1,18 +0,0 @@ -from typing import Callable, Union, Tuple, Optional - -__all__ = ['generate', 'construct', 'ElGamalKey'] - -RNG = Callable[[int], bytes] - -def generate(bits: int, randfunc: RNG) -> ElGamalKey: ... -def construct(tup: Union[Tuple[int, int, int], Tuple[int, int, int, int]]) -> ElGamalKey: ... - -class ElGamalKey(object): - def __init__(self, randfunc: Optional[RNG]=None) -> None: ... - def has_private(self) -> bool: ... - def can_encrypt(self) -> bool: ... - def can_sign(self) -> bool: ... - def publickey(self) -> ElGamalKey: ... - def __eq__(self, other: object) -> bool: ... - def __ne__(self, other: object) -> bool: ... - def __getstate__(self) -> None: ... diff --git a/resources/lib/deps/Cryptodome/PublicKey/RSA.py b/resources/lib/deps/Cryptodome/PublicKey/RSA.py deleted file mode 100644 index 9a27c36..0000000 --- a/resources/lib/deps/Cryptodome/PublicKey/RSA.py +++ /dev/null @@ -1,864 +0,0 @@ -# -*- coding: utf-8 -*- -# =================================================================== -# -# Copyright (c) 2016, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -__all__ = ['generate', 'construct', 'import_key', - 'RsaKey', 'oid'] - -import binascii -import struct - -from Cryptodome import Random -from Cryptodome.Util.py3compat import tobytes, bord, tostr -from Cryptodome.Util.asn1 import DerSequence, DerNull -from Cryptodome.Util.number import bytes_to_long - -from Cryptodome.Math.Numbers import Integer -from Cryptodome.Math.Primality import (test_probable_prime, - generate_probable_prime, COMPOSITE) - -from Cryptodome.PublicKey import (_expand_subject_public_key_info, - _create_subject_public_key_info, - _extract_subject_public_key_info) - - -class RsaKey(object): - r"""Class defining an RSA key, private or public. - Do not instantiate directly. - Use :func:`generate`, :func:`construct` or :func:`import_key` instead. - - :ivar n: RSA modulus - :vartype n: integer - - :ivar e: RSA public exponent - :vartype e: integer - - :ivar d: RSA private exponent - :vartype d: integer - - :ivar p: First factor of the RSA modulus - :vartype p: integer - - :ivar q: Second factor of the RSA modulus - :vartype q: integer - - :ivar invp: Chinese remainder component (:math:`p^{-1} \text{mod } q`) - :vartype invp: integer - - :ivar invq: Chinese remainder component (:math:`q^{-1} \text{mod } p`) - :vartype invq: integer - - :ivar u: Same as ``invp`` - :vartype u: integer - """ - - def __init__(self, **kwargs): - """Build an RSA key. - - :Keywords: - n : integer - The modulus. - e : integer - The public exponent. - d : integer - The private exponent. Only required for private keys. - p : integer - The first factor of the modulus. Only required for private keys. - q : integer - The second factor of the modulus. Only required for private keys. - u : integer - The CRT coefficient (inverse of p modulo q). Only required for - private keys. - """ - - input_set = set(kwargs.keys()) - public_set = set(('n', 'e')) - private_set = public_set | set(('p', 'q', 'd', 'u')) - if input_set not in (private_set, public_set): - raise ValueError("Some RSA components are missing") - for component, value in kwargs.items(): - setattr(self, "_" + component, value) - if input_set == private_set: - self._dp = self._d % (self._p - 1) # = (e⁻¹) mod (p-1) - self._dq = self._d % (self._q - 1) # = (e⁻¹) mod (q-1) - self._invq = None # will be computed on demand - - @property - def n(self): - return int(self._n) - - @property - def e(self): - return int(self._e) - - @property - def d(self): - if not self.has_private(): - raise AttributeError("No private exponent available for public keys") - return int(self._d) - - @property - def p(self): - if not self.has_private(): - raise AttributeError("No CRT component 'p' available for public keys") - return int(self._p) - - @property - def q(self): - if not self.has_private(): - raise AttributeError("No CRT component 'q' available for public keys") - return int(self._q) - - @property - def dp(self): - if not self.has_private(): - raise AttributeError("No CRT component 'dp' available for public keys") - return int(self._dp) - - @property - def dq(self): - if not self.has_private(): - raise AttributeError("No CRT component 'dq' available for public keys") - return int(self._dq) - - @property - def invq(self): - if not self.has_private(): - raise AttributeError("No CRT component 'invq' available for public keys") - if self._invq is None: - self._invq = self._q.inverse(self._p) - return int(self._invq) - - @property - def invp(self): - return self.u - - @property - def u(self): - if not self.has_private(): - raise AttributeError("No CRT component 'u' available for public keys") - return int(self._u) - - def size_in_bits(self): - """Size of the RSA modulus in bits""" - return self._n.size_in_bits() - - def size_in_bytes(self): - """The minimal amount of bytes that can hold the RSA modulus""" - return (self._n.size_in_bits() - 1) // 8 + 1 - - def _encrypt(self, plaintext): - if not 0 <= plaintext < self._n: - raise ValueError("Plaintext too large") - return int(pow(Integer(plaintext), self._e, self._n)) - - def _decrypt_to_bytes(self, ciphertext): - if not 0 <= ciphertext < self._n: - raise ValueError("Ciphertext too large") - if not self.has_private(): - raise TypeError("This is not a private key") - - # Blinded RSA decryption (to prevent timing attacks): - # Step 1: Generate random secret blinding factor r, - # such that 0 < r < n-1 - r = Integer.random_range(min_inclusive=1, max_exclusive=self._n) - # Step 2: Compute c' = c * r**e mod n - cp = Integer(ciphertext) * pow(r, self._e, self._n) % self._n - # Step 3: Compute m' = c'**d mod n (normal RSA decryption) - m1 = pow(cp, self._dp, self._p) - m2 = pow(cp, self._dq, self._q) - h = ((m2 - m1) * self._u) % self._q - mp = h * self._p + m1 - # Step 4: Compute m = m' * (r**(-1)) mod n - # then encode into a big endian byte string - result = Integer._mult_modulo_bytes( - r.inverse(self._n), - mp, - self._n) - return result - - def _decrypt(self, ciphertext): - """Legacy private method""" - - return bytes_to_long(self._decrypt_to_bytes(ciphertext)) - - def has_private(self): - """Whether this is an RSA private key""" - - return hasattr(self, "_d") - - def can_encrypt(self): # legacy - return True - - def can_sign(self): # legacy - return True - - def public_key(self): - """A matching RSA public key. - - Returns: - a new :class:`RsaKey` object - """ - return RsaKey(n=self._n, e=self._e) - - def __eq__(self, other): - if self.has_private() != other.has_private(): - return False - if self.n != other.n or self.e != other.e: - return False - if not self.has_private(): - return True - return (self.d == other.d) - - def __ne__(self, other): - return not (self == other) - - def __getstate__(self): - # RSA key is not pickable - from pickle import PicklingError - raise PicklingError - - def __repr__(self): - if self.has_private(): - extra = ", d=%d, p=%d, q=%d, u=%d" % (int(self._d), int(self._p), - int(self._q), int(self._u)) - else: - extra = "" - return "RsaKey(n=%d, e=%d%s)" % (int(self._n), int(self._e), extra) - - def __str__(self): - if self.has_private(): - key_type = "Private" - else: - key_type = "Public" - return "%s RSA key at 0x%X" % (key_type, id(self)) - - def export_key(self, format='PEM', passphrase=None, pkcs=1, - protection=None, randfunc=None, prot_params=None): - """Export this RSA key. - - Keyword Args: - format (string): - The desired output format: - - - ``'PEM'``. (default) Text output, according to `RFC1421`_/`RFC1423`_. - - ``'DER'``. Binary output. - - ``'OpenSSH'``. Text output, according to the OpenSSH specification. - Only suitable for public keys (not private keys). - - Note that PEM contains a DER structure. - - passphrase (bytes or string): - (*Private keys only*) The passphrase to protect the - private key. - - pkcs (integer): - (*Private keys only*) The standard to use for - serializing the key: PKCS#1 or PKCS#8. - - With ``pkcs=1`` (*default*), the private key is encoded with a - simple `PKCS#1`_ structure (``RSAPrivateKey``). The key cannot be - securely encrypted. - - With ``pkcs=8``, the private key is encoded with a `PKCS#8`_ structure - (``PrivateKeyInfo``). PKCS#8 offers the best ways to securely - encrypt the key. - - .. note:: - This parameter is ignored for a public key. - For DER and PEM, the output is always an - ASN.1 DER ``SubjectPublicKeyInfo`` structure. - - protection (string): - (*For private keys only*) - The encryption scheme to use for protecting the private key - using the passphrase. - - You can only specify a value if ``pkcs=8``. - For all possible protection schemes, - refer to :ref:`the encryption parameters of PKCS#8`. - The recommended value is - ``'PBKDF2WithHMAC-SHA512AndAES256-CBC'``. - - If ``None`` (default), the behavior depends on :attr:`format`: - - - if ``format='PEM'``, the obsolete PEM encryption scheme is used. - It is based on MD5 for key derivation, and 3DES for encryption. - - - if ``format='DER'``, the ``'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC'`` - scheme is used. - - prot_params (dict): - (*For private keys only*) - - The parameters to use to derive the encryption key - from the passphrase. ``'protection'`` must be also specified. - For all possible values, - refer to :ref:`the encryption parameters of PKCS#8`. - The recommendation is to use ``{'iteration_count':21000}`` for PBKDF2, - and ``{'iteration_count':131072}`` for scrypt. - - randfunc (callable): - A function that provides random bytes. Only used for PEM encoding. - The default is :func:`Cryptodome.Random.get_random_bytes`. - - Returns: - bytes: the encoded key - - Raises: - ValueError:when the format is unknown or when you try to encrypt a private - key with *DER* format and PKCS#1. - - .. warning:: - If you don't provide a pass phrase, the private key will be - exported in the clear! - - .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt - .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt - .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt - .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt - """ - - if passphrase is not None: - passphrase = tobytes(passphrase) - - if randfunc is None: - randfunc = Random.get_random_bytes - - if format == 'OpenSSH': - e_bytes, n_bytes = [x.to_bytes() for x in (self._e, self._n)] - if bord(e_bytes[0]) & 0x80: - e_bytes = b'\x00' + e_bytes - if bord(n_bytes[0]) & 0x80: - n_bytes = b'\x00' + n_bytes - keyparts = [b'ssh-rsa', e_bytes, n_bytes] - keystring = b''.join([struct.pack(">I", len(kp)) + kp for kp in keyparts]) - return b'ssh-rsa ' + binascii.b2a_base64(keystring)[:-1] - - # DER format is always used, even in case of PEM, which simply - # encodes it into BASE64. - if self.has_private(): - binary_key = DerSequence([0, - self.n, - self.e, - self.d, - self.p, - self.q, - self.d % (self.p-1), - self.d % (self.q-1), - Integer(self.q).inverse(self.p) - ]).encode() - if pkcs == 1: - key_type = 'RSA PRIVATE KEY' - if format == 'DER' and passphrase: - raise ValueError("PKCS#1 private key cannot be encrypted") - else: # PKCS#8 - from Cryptodome.IO import PKCS8 - - if format == 'PEM' and protection is None: - key_type = 'PRIVATE KEY' - binary_key = PKCS8.wrap(binary_key, oid, None, - key_params=DerNull()) - else: - key_type = 'ENCRYPTED PRIVATE KEY' - if not protection: - if prot_params: - raise ValueError("'protection' parameter must be set") - protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' - binary_key = PKCS8.wrap(binary_key, oid, - passphrase, protection, - prot_params=prot_params, - key_params=DerNull()) - passphrase = None - else: - key_type = "PUBLIC KEY" - binary_key = _create_subject_public_key_info(oid, - DerSequence([self.n, - self.e]), - DerNull() - ) - - if format == 'DER': - return binary_key - if format == 'PEM': - from Cryptodome.IO import PEM - - pem_str = PEM.encode(binary_key, key_type, passphrase, randfunc) - return tobytes(pem_str) - - raise ValueError("Unknown key format '%s'. Cannot export the RSA key." % format) - - # Backward compatibility - def exportKey(self, *args, **kwargs): - """:meta private:""" - return self.export_key(*args, **kwargs) - - def publickey(self): - """:meta private:""" - return self.public_key() - - # Methods defined in PyCryptodome that we don't support anymore - def sign(self, M, K): - """:meta private:""" - raise NotImplementedError("Use module Cryptodome.Signature.pkcs1_15 instead") - - def verify(self, M, signature): - """:meta private:""" - raise NotImplementedError("Use module Cryptodome.Signature.pkcs1_15 instead") - - def encrypt(self, plaintext, K): - """:meta private:""" - raise NotImplementedError("Use module Cryptodome.Cipher.PKCS1_OAEP instead") - - def decrypt(self, ciphertext): - """:meta private:""" - raise NotImplementedError("Use module Cryptodome.Cipher.PKCS1_OAEP instead") - - def blind(self, M, B): - """:meta private:""" - raise NotImplementedError - - def unblind(self, M, B): - """:meta private:""" - raise NotImplementedError - - def size(self): - """:meta private:""" - raise NotImplementedError - - -def generate(bits, randfunc=None, e=65537): - """Create a new RSA key pair. - - The algorithm closely follows NIST `FIPS 186-4`_ in its - sections B.3.1 and B.3.3. The modulus is the product of - two non-strong probable primes. - Each prime passes a suitable number of Miller-Rabin tests - with random bases and a single Lucas test. - - Args: - bits (integer): - Key length, or size (in bits) of the RSA modulus. - It must be at least 1024, but **2048 is recommended.** - The FIPS standard only defines 1024, 2048 and 3072. - Keyword Args: - randfunc (callable): - Function that returns random bytes. - The default is :func:`Cryptodome.Random.get_random_bytes`. - e (integer): - Public RSA exponent. It must be an odd positive integer. - It is typically a small number with very few ones in its - binary representation. - The FIPS standard requires the public exponent to be - at least 65537 (the default). - - Returns: an RSA key object (:class:`RsaKey`, with private key). - - .. _FIPS 186-4: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf - """ - - if bits < 1024: - raise ValueError("RSA modulus length must be >= 1024") - if e % 2 == 0 or e < 3: - raise ValueError("RSA public exponent must be a positive, odd integer larger than 2.") - - if randfunc is None: - randfunc = Random.get_random_bytes - - d = n = Integer(1) - e = Integer(e) - - while n.size_in_bits() != bits and d < (1 << (bits // 2)): - # Generate the prime factors of n: p and q. - # By construciton, their product is always - # 2^{bits-1} < p*q < 2^bits. - size_q = bits // 2 - size_p = bits - size_q - - min_p = min_q = (Integer(1) << (2 * size_q - 1)).sqrt() - if size_q != size_p: - min_p = (Integer(1) << (2 * size_p - 1)).sqrt() - - def filter_p(candidate): - return candidate > min_p and (candidate - 1).gcd(e) == 1 - - p = generate_probable_prime(exact_bits=size_p, - randfunc=randfunc, - prime_filter=filter_p) - - min_distance = Integer(1) << (bits // 2 - 100) - - def filter_q(candidate): - return (candidate > min_q and - (candidate - 1).gcd(e) == 1 and - abs(candidate - p) > min_distance) - - q = generate_probable_prime(exact_bits=size_q, - randfunc=randfunc, - prime_filter=filter_q) - - n = p * q - lcm = (p - 1).lcm(q - 1) - d = e.inverse(lcm) - - if p > q: - p, q = q, p - - u = p.inverse(q) - - return RsaKey(n=n, e=e, d=d, p=p, q=q, u=u) - - -def construct(rsa_components, consistency_check=True): - r"""Construct an RSA key from a tuple of valid RSA components. - - The modulus **n** must be the product of two primes. - The public exponent **e** must be odd and larger than 1. - - In case of a private key, the following equations must apply: - - .. math:: - - \begin{align} - p*q &= n \\ - e*d &\equiv 1 ( \text{mod lcm} [(p-1)(q-1)]) \\ - p*u &\equiv 1 ( \text{mod } q) - \end{align} - - Args: - rsa_components (tuple): - A tuple of integers, with at least 2 and no - more than 6 items. The items come in the following order: - - 1. RSA modulus *n*. - 2. Public exponent *e*. - 3. Private exponent *d*. - Only required if the key is private. - 4. First factor of *n* (*p*). - Optional, but the other factor *q* must also be present. - 5. Second factor of *n* (*q*). Optional. - 6. CRT coefficient *q*, that is :math:`p^{-1} \text{mod }q`. Optional. - - Keyword Args: - consistency_check (boolean): - If ``True``, the library will verify that the provided components - fulfil the main RSA properties. - - Raises: - ValueError: when the key being imported fails the most basic RSA validity checks. - - Returns: An RSA key object (:class:`RsaKey`). - """ - - class InputComps(object): - pass - - input_comps = InputComps() - for (comp, value) in zip(('n', 'e', 'd', 'p', 'q', 'u'), rsa_components): - setattr(input_comps, comp, Integer(value)) - - n = input_comps.n - e = input_comps.e - if not hasattr(input_comps, 'd'): - key = RsaKey(n=n, e=e) - else: - d = input_comps.d - if hasattr(input_comps, 'q'): - p = input_comps.p - q = input_comps.q - else: - # Compute factors p and q from the private exponent d. - # We assume that n has no more than two factors. - # See 8.2.2(i) in Handbook of Applied Cryptography. - ktot = d * e - 1 - # The quantity d*e-1 is a multiple of phi(n), even, - # and can be represented as t*2^s. - t = ktot - while t % 2 == 0: - t //= 2 - # Cycle through all multiplicative inverses in Zn. - # The algorithm is non-deterministic, but there is a 50% chance - # any candidate a leads to successful factoring. - # See "Digitalized Signatures and Public Key Functions as Intractable - # as Factorization", M. Rabin, 1979 - spotted = False - a = Integer(2) - while not spotted and a < 100: - k = Integer(t) - # Cycle through all values a^{t*2^i}=a^k - while k < ktot: - cand = pow(a, k, n) - # Check if a^k is a non-trivial root of unity (mod n) - if cand != 1 and cand != (n - 1) and pow(cand, 2, n) == 1: - # We have found a number such that (cand-1)(cand+1)=0 (mod n). - # Either of the terms divides n. - p = Integer(n).gcd(cand + 1) - spotted = True - break - k *= 2 - # This value was not any good... let's try another! - a += 2 - if not spotted: - raise ValueError("Unable to compute factors p and q from exponent d.") - # Found ! - assert ((n % p) == 0) - q = n // p - - if hasattr(input_comps, 'u'): - u = input_comps.u - else: - u = p.inverse(q) - - # Build key object - key = RsaKey(n=n, e=e, d=d, p=p, q=q, u=u) - - # Verify consistency of the key - if consistency_check: - - # Modulus and public exponent must be coprime - if e <= 1 or e >= n: - raise ValueError("Invalid RSA public exponent") - if Integer(n).gcd(e) != 1: - raise ValueError("RSA public exponent is not coprime to modulus") - - # For RSA, modulus must be odd - if not n & 1: - raise ValueError("RSA modulus is not odd") - - if key.has_private(): - # Modulus and private exponent must be coprime - if d <= 1 or d >= n: - raise ValueError("Invalid RSA private exponent") - if Integer(n).gcd(d) != 1: - raise ValueError("RSA private exponent is not coprime to modulus") - # Modulus must be product of 2 primes - if p * q != n: - raise ValueError("RSA factors do not match modulus") - if test_probable_prime(p) == COMPOSITE: - raise ValueError("RSA factor p is composite") - if test_probable_prime(q) == COMPOSITE: - raise ValueError("RSA factor q is composite") - # See Carmichael theorem - phi = (p - 1) * (q - 1) - lcm = phi // (p - 1).gcd(q - 1) - if (e * d % int(lcm)) != 1: - raise ValueError("Invalid RSA condition") - if hasattr(key, 'u'): - # CRT coefficient - if u <= 1 or u >= q: - raise ValueError("Invalid RSA component u") - if (p * u % q) != 1: - raise ValueError("Invalid RSA component u with p") - - return key - - -def _import_pkcs1_private(encoded, *kwargs): - # RSAPrivateKey ::= SEQUENCE { - # version Version, - # modulus INTEGER, -- n - # publicExponent INTEGER, -- e - # privateExponent INTEGER, -- d - # prime1 INTEGER, -- p - # prime2 INTEGER, -- q - # exponent1 INTEGER, -- d mod (p-1) - # exponent2 INTEGER, -- d mod (q-1) - # coefficient INTEGER -- (inverse of q) mod p - # } - # - # Version ::= INTEGER - der = DerSequence().decode(encoded, nr_elements=9, only_ints_expected=True) - if der[0] != 0: - raise ValueError("No PKCS#1 encoding of an RSA private key") - return construct(der[1:6] + [Integer(der[4]).inverse(der[5])]) - - -def _import_pkcs1_public(encoded, *kwargs): - # RSAPublicKey ::= SEQUENCE { - # modulus INTEGER, -- n - # publicExponent INTEGER -- e - # } - der = DerSequence().decode(encoded, nr_elements=2, only_ints_expected=True) - return construct(der) - - -def _import_subjectPublicKeyInfo(encoded, *kwargs): - - algoid, encoded_key, params = _expand_subject_public_key_info(encoded) - if algoid != oid or params is not None: - raise ValueError("No RSA subjectPublicKeyInfo") - return _import_pkcs1_public(encoded_key) - - -def _import_x509_cert(encoded, *kwargs): - - sp_info = _extract_subject_public_key_info(encoded) - return _import_subjectPublicKeyInfo(sp_info) - - -def _import_pkcs8(encoded, passphrase): - from Cryptodome.IO import PKCS8 - - k = PKCS8.unwrap(encoded, passphrase) - if k[0] != oid: - raise ValueError("No PKCS#8 encoded RSA key") - return _import_keyDER(k[1], passphrase) - - -def _import_keyDER(extern_key, passphrase): - """Import an RSA key (public or private half), encoded in DER form.""" - - decodings = (_import_pkcs1_private, - _import_pkcs1_public, - _import_subjectPublicKeyInfo, - _import_x509_cert, - _import_pkcs8) - - for decoding in decodings: - try: - return decoding(extern_key, passphrase) - except ValueError: - pass - - raise ValueError("RSA key format is not supported") - - -def _import_openssh_private_rsa(data, password): - - from ._openssh import (import_openssh_private_generic, - read_bytes, read_string, check_padding) - - ssh_name, decrypted = import_openssh_private_generic(data, password) - - if ssh_name != "ssh-rsa": - raise ValueError("This SSH key is not RSA") - - n, decrypted = read_bytes(decrypted) - e, decrypted = read_bytes(decrypted) - d, decrypted = read_bytes(decrypted) - iqmp, decrypted = read_bytes(decrypted) - p, decrypted = read_bytes(decrypted) - q, decrypted = read_bytes(decrypted) - - _, padded = read_string(decrypted) # Comment - check_padding(padded) - - build = [Integer.from_bytes(x) for x in (n, e, d, q, p, iqmp)] - return construct(build) - - -def import_key(extern_key, passphrase=None): - """Import an RSA key (public or private). - - Args: - extern_key (string or byte string): - The RSA key to import. - - The following formats are supported for an RSA **public key**: - - - X.509 certificate (binary or PEM format) - - X.509 ``subjectPublicKeyInfo`` DER SEQUENCE (binary or PEM - encoding) - - `PKCS#1`_ ``RSAPublicKey`` DER SEQUENCE (binary or PEM encoding) - - An OpenSSH line (e.g. the content of ``~/.ssh/id_ecdsa``, ASCII) - - The following formats are supported for an RSA **private key**: - - - PKCS#1 ``RSAPrivateKey`` DER SEQUENCE (binary or PEM encoding) - - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` - DER SEQUENCE (binary or PEM encoding) - - OpenSSH (text format, introduced in `OpenSSH 6.5`_) - - For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. - - passphrase (string or byte string): - For private keys only, the pass phrase that encrypts the key. - - Returns: An RSA key object (:class:`RsaKey`). - - Raises: - ValueError/IndexError/TypeError: - When the given key cannot be parsed (possibly because the pass - phrase is wrong). - - .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt - .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt - .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt - .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt - .. _`OpenSSH 6.5`: https://flak.tedunangst.com/post/new-openssh-key-format-and-bcrypt-pbkdf - """ - - from Cryptodome.IO import PEM - - extern_key = tobytes(extern_key) - if passphrase is not None: - passphrase = tobytes(passphrase) - - if extern_key.startswith(b'-----BEGIN OPENSSH PRIVATE KEY'): - text_encoded = tostr(extern_key) - openssh_encoded, marker, enc_flag = PEM.decode(text_encoded, passphrase) - result = _import_openssh_private_rsa(openssh_encoded, passphrase) - return result - - if extern_key.startswith(b'-----'): - # This is probably a PEM encoded key. - (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) - if enc_flag: - passphrase = None - return _import_keyDER(der, passphrase) - - if extern_key.startswith(b'ssh-rsa '): - # This is probably an OpenSSH key - keystring = binascii.a2b_base64(extern_key.split(b' ')[1]) - keyparts = [] - while len(keystring) > 4: - length = struct.unpack(">I", keystring[:4])[0] - keyparts.append(keystring[4:4 + length]) - keystring = keystring[4 + length:] - e = Integer.from_bytes(keyparts[1]) - n = Integer.from_bytes(keyparts[2]) - return construct([n, e]) - - if len(extern_key) > 0 and bord(extern_key[0]) == 0x30: - # This is probably a DER encoded key - return _import_keyDER(extern_key, passphrase) - - raise ValueError("RSA key format is not supported") - - -# Backward compatibility -importKey = import_key - -#: `Object ID`_ for the RSA encryption algorithm. This OID often indicates -#: a generic RSA key, even when such key will be actually used for digital -#: signatures. -#: -#: .. _`Object ID`: http://www.alvestrand.no/objectid/1.2.840.113549.1.1.1.html -oid = "1.2.840.113549.1.1.1" diff --git a/resources/lib/deps/Cryptodome/PublicKey/RSA.pyi b/resources/lib/deps/Cryptodome/PublicKey/RSA.pyi deleted file mode 100644 index 85f6c4a..0000000 --- a/resources/lib/deps/Cryptodome/PublicKey/RSA.pyi +++ /dev/null @@ -1,78 +0,0 @@ -from typing import Callable, Union, Tuple, Optional, overload, Literal - -from Cryptodome.Math.Numbers import Integer -from Cryptodome.IO._PBES import ProtParams - -__all__ = ['generate', 'construct', 'import_key', - 'RsaKey', 'oid'] - -RNG = Callable[[int], bytes] - -class RsaKey(object): - def __init__(self, **kwargs: int) -> None: ... - - @property - def n(self) -> int: ... - @property - def e(self) -> int: ... - @property - def d(self) -> int: ... - @property - def p(self) -> int: ... - @property - def q(self) -> int: ... - @property - def u(self) -> int: ... - @property - def invp(self) -> int: ... - @property - def invq(self) -> int: ... - - def size_in_bits(self) -> int: ... - def size_in_bytes(self) -> int: ... - def has_private(self) -> bool: ... - def can_encrypt(self) -> bool: ... # legacy - def can_sign(self) -> bool:... # legacy - def public_key(self) -> RsaKey: ... - def __eq__(self, other: object) -> bool: ... - def __ne__(self, other: object) -> bool: ... - def __getstate__(self) -> None: ... - def __repr__(self) -> str: ... - def __str__(self) -> str: ... - - @overload - def export_key(self, - format: Optional[str]="PEM", - passphrase: Optional[str]=None, - pkcs: Optional[int]=1, - protection: Optional[str]=None, - randfunc: Optional[RNG]=None - ) -> bytes: ... - @overload - def export_key(self, *, - format: Optional[str]="PEM", - passphrase: str, - pkcs: Literal[8], - protection: str, - randfunc: Optional[RNG]=None, - prot_params: ProtParams, - ) -> bytes: ... - - # Backward compatibility - exportKey = export_key - publickey = public_key - -Int = Union[int, Integer] - -def generate(bits: int, randfunc: Optional[RNG]=None, e: Optional[int]=65537) -> RsaKey: ... -def construct(rsa_components: Union[Tuple[Int, Int], # n, e - Tuple[Int, Int, Int], # n, e, d - Tuple[Int, Int, Int, Int, Int], # n, e, d, p, q - Tuple[Int, Int, Int, Int, Int, Int]], # n, e, d, p, q, crt_q - consistency_check: Optional[bool]=True) -> RsaKey: ... -def import_key(extern_key: Union[str, bytes], passphrase: Optional[str]=None) -> RsaKey: ... - -# Backward compatibility -importKey = import_key - -oid: str diff --git a/resources/lib/deps/Cryptodome/PublicKey/__init__.py b/resources/lib/deps/Cryptodome/PublicKey/__init__.py deleted file mode 100644 index 99b67a4..0000000 --- a/resources/lib/deps/Cryptodome/PublicKey/__init__.py +++ /dev/null @@ -1,94 +0,0 @@ -# -*- coding: utf-8 -*- -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -from Cryptodome.Util.asn1 import (DerSequence, DerInteger, DerBitString, - DerObjectId, DerNull) - - -def _expand_subject_public_key_info(encoded): - """Parse a SubjectPublicKeyInfo structure. - - It returns a triple with: - * OID (string) - * encoded public key (bytes) - * Algorithm parameters (bytes or None) - """ - - # - # SubjectPublicKeyInfo ::= SEQUENCE { - # algorithm AlgorithmIdentifier, - # subjectPublicKey BIT STRING - # } - # - # AlgorithmIdentifier ::= SEQUENCE { - # algorithm OBJECT IDENTIFIER, - # parameters ANY DEFINED BY algorithm OPTIONAL - # } - # - - spki = DerSequence().decode(encoded, nr_elements=2) - algo = DerSequence().decode(spki[0], nr_elements=(1,2)) - algo_oid = DerObjectId().decode(algo[0]) - spk = DerBitString().decode(spki[1]).value - - if len(algo) == 1: - algo_params = None - else: - try: - DerNull().decode(algo[1]) - algo_params = None - except: - algo_params = algo[1] - - return algo_oid.value, spk, algo_params - - -def _create_subject_public_key_info(algo_oid, public_key, params): - - if params is None: - algorithm = DerSequence([DerObjectId(algo_oid)]) - else: - algorithm = DerSequence([DerObjectId(algo_oid), params]) - - spki = DerSequence([algorithm, - DerBitString(public_key) - ]) - return spki.encode() - - -def _extract_subject_public_key_info(x509_certificate): - """Extract subjectPublicKeyInfo from a DER X.509 certificate.""" - - certificate = DerSequence().decode(x509_certificate, nr_elements=3) - tbs_certificate = DerSequence().decode(certificate[0], - nr_elements=range(6, 11)) - - index = 5 - try: - tbs_certificate[0] + 1 - # Version not present - version = 1 - except TypeError: - version = DerInteger(explicit=0).decode(tbs_certificate[0]).value - if version not in (2, 3): - raise ValueError("Incorrect X.509 certificate version") - index = 6 - - return tbs_certificate[index] diff --git a/resources/lib/deps/Cryptodome/PublicKey/__init__.pyi b/resources/lib/deps/Cryptodome/PublicKey/__init__.pyi deleted file mode 100644 index e69de29..0000000 diff --git a/resources/lib/deps/Cryptodome/PublicKey/_ec_ws.abi3.so b/resources/lib/deps/Cryptodome/PublicKey/_ec_ws.abi3.so deleted file mode 100755 index b1272d2e5a2ec06a857456d05eb8c7af7143642d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 960552 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%0Ay0HNrCaA2uU zq@2)R$C0p*s8@Wr+i;_5T0+cg?T+`H>dwYAXF3y=;nAnM4Zt0)Rg1j%E$w=X66bRz z=`1qDNahQa;(y9(Y^J;J$4~x9uPWup_YWsPFZNd3Z`G5$9-lNmFHN&PNEHq+%47v&0hmc!_Y zWe(N2^X+NuW2f#s86N+B5hHayx=oAbU10v8li68Ld+fDXT|EakP#?Cb+vB7we+A5aM7y;Ms>0d=H{s$0Tuh+>f;)e!$ecC@8(_YFk7cHO^x zG2F*u=y_X{e3=`xm zJ~or@oL)0phLzxzcCrwg$0-Jnkv0n9H@dU3_!##VX3gt;mgv{2VzB+Aw!5%8@F&nJ zdQ4stM33~YI9iI~EeYG;a9!ntzX z6tIZe!{cXCmg-0KiV5nyq!f}W`~iKHUU5;7xl*4e7rg(kz3xebr>L-#(S* zuf%qjkjjb6a9ytg+ITGxjiuBm!5~j==dNeJ+3W%E*!0OHIK`sBm#eJGyhxJ(hS&=- z3>gV5J{6sbF`8{b>GxpPNoLAG2<9JLxx5|~!HchghTm#GdrdS%03Fj|J?FP2yl zSuAWLU$rW%l+}NJ1fng+F+ZhNT;&8eCQB9vUP%}M> z06=ILmhz%G9FuWx7MX%ZUEn~AWVyeAFX3UUx+d4QdTll0Yox_CqXCmZ+7{>}%M2xC zF<-%*;(xoU5uBW2gf_0KusC(vQq(LaDWIB%L(m!wD5Mg4d3LV^E_wc0l2BuPueDSL zEH{uQpQmRqL{jNYKx~K>+tTw{CJF?o%i#A$-0eMql-L|HP%%2Xxyenr~l1uRo> z*mN`4a`^{H28AfIsNqEaUCclt7JwZ}2na@kv?6eSQM)*sy%deMZ#{^47NPci@n9}x z(y@6{%0LgcfYKnDQJTWMWSjh5O#Gdb52)P)xu)lapmf1gWip68!micWA4{=ycBV^# zH#TWE7;6Q4%12q~1G_emAJpaiA-nkcc8qOep+Ib;S}G$cv)eWMvf$9K_$@Mt#SJ59)d2E1E^$n_`Vbt_=aV$q5`aRN5U z{|Uel`^pfCJbT_-$}JXopLFyXm5Ds9i3UdMb zdGQ-NDc}3Ph`-eNb*B4Dw$lIHetBATE#{++&?U-cFdJf84`{pUG~@FqZsCEmVI_cg!P~{4eu%%mJCa;oa=3!cDnYCzkGtT8?W1|*akCeEYxLuoL5drf${Lg}=ooqc!paPVp8*IS6o~oclqi?s=6uw; z3p7Xo&;($YHMv2wVigou5Ufy_BFYd`kG}M^VMvL3o4Cf8T|JRi1h~MY^>K{vNboXd$#i zSSaEUc3DOE3M0ba|0AT9sPr_J8la;ceV=$8H5cg4B$@ngGb?t-kgS6%6CGbGx!(_^ zYWxM7X4KKPP2P>=AmaILgu&;%5mNzughda4%>BQA;Kn~$IX}9v(TCX0d-o1sKL@#( z0|ZsqzIOLXya7R*X}HQ(NeTog_QB{E#v>5-LC>O6vo)0brkG4LX-_Vrqn$oC4EWvlY262ZAa;! zNwdD?f&w;X7n*iF)`zxV07wxTRNrdu6O8GwDo*ZO17Cf;@;?C92d;u6N-8QmWqHR; zn*%TcP@jnAXuo{g3klZNFkWkA?ln7i3ZPCP}0o ziq3EMOQbmGTpq+Obo!aHj5{LkxS!o1un|7$EQxTcKM~IlOFvs?0>;W(da@_^U}mv1 zfyq_)uJ-;xrg5B%^75XW7t(l-{c-=yHD`G568=x?3iskPKaolPnDgQYlZx$y$^ zJy6)d`ni}wy*AT`wsB8duE`0$Op=tMuJ>ZS^z8!ldlOAeAw5Q3vNCIBr96j1Dx<8I z6sct=em-6^IQ^6V#NHb4mUhYGMGoLA)9}?WxF(4j+(Lt#ZJf&qMyIQcraZr~?Rh{? zfS62xCi+I(wT_s$FK~e}`K74Kj>$~;VEwTUTG+N*C6@={oR9rqTEIeeKI>b;Bwa}M zbSW*m&S75uKV1EPs!W{G&-QU|pvd3RBg1_XAK;h1lRnV!{Wr!}J8N9Beo)h zq5EIu!wKggdrOutS6H@IUH5o+Vu)H6Q1_iEy%9{-YrMm$y(wF1`L z=%WS9)Iif&BZH#Uf*-5E?1kmX+H!Ao=;TrP2>yMwD$_(ebZ}{^9S#?Ps@>s0?cKKL znYbILryLl0(R|~^pyt4;9EFS+Du?9RAXF|%=R@iRr6;q%&_;r(AXX+c2i-zVF9`T` z>c4^yk5K%y@Ox5v<^P?TGFM#o0VmBj+ykRRjHO`Z)21PG@=P2HU6ektH`gTgim{sZ zod~*h0qnk=Sg9ee$>wsC4!(oYZ?H~HPD)!i?N^~6a5k5`(Xk?h%{(a0ax2|KH#j_wWOMMa2iR#DbQ@6ag*MB1{wr*Ly9Up`Dfknd_d3)+ZoGam)cZVqX=@`Q zp0>I!^(?zJ>hyz}bvY=Ne{9ezHp|=QKFLn#PLk(%Wq5>2GMgxzL>@?UD>L-1CcE{w zRlJUDR3|+>J-Gj!XsUNNFzSuhv)Na4tD94py+yCPPR6tKpS+njCKq9v-Km_WyOtNC z1Z+bzr`(RxsD6NVO(hrTTc9+N0R=@UOk7q#gxZ5gWOCr}tAR(*$B0f$i8f@q_xjJx z%lh8tD#K079nGUH)Jb7_h%~ks%{Y8Rmzm*i75un4{-U!gl*HFc1U}PtLZhTKn+Qzs z>M&b`>l{O-I2mF7b_-=w&qv!@DlR^(BPg~{7MbK$A`e(XATlrZds1_^q zc0W3F2X#7A%vDaps#AOCi|&|)D{4*^5<PjHzkstkTv}9=ekUd> zCwgscp_*r%IK{Qhe?f7cP$}6M2jAVPaBPFOgMJU_?#0y3+GV|Bi?w+y0LPG*UpP)G zPxC*q%>-bT+Fcw)187+kQwWVarkfCBcP&uCkHN8;b*x}+_Plt?a%^++4CU;sGMONg z=qN$Y&64Ot?7upb~YdyP{f^$>M*~n7{#?;y2ujURwV~C@bB(q>A+sSsAPhJ&&+zgnlB!=7C zyvAQoquK5;riI0MjLgj;Snpa^2tLA%G7!#>i|UL<04u}fdGwU1wXf!V8I%2o>=i1S zC)zmF?C{$Mb(#E%P&RyjW4U^xp1votek%b8lw@=CO z0z%&I0Bfk)%sb;v#7XK{Sn(xEleVBt3CSbz>T!*uHEz#mX=+y<#3SNaus&}adRf2h z?tw@b35|?KjsTt~;kHLwbW+w4PbxFv5zB7_8UBKf0BMa z^}yqxU&UDX?QH`bA}v4yMh@XV<-5Ut%(N$MrV_WP07vUt7Pv7YC|q`x66#5_hWwr* zmv#vU%d|>SN>|dWNawpY7y=43wEt)Q|K|I&XzWD^_Nqtkd8KFS+&+`(-yWd$d%zVn zvOoG8yr+ zPd1Z(24ZfI%#=Z(^SWP+uyV8@30yzAdXw+=H={pt(bE+)Ezml~$9OmZ6HzazDWHys zvfcONioyBmM~nHAKf4lg2%NuK0eZUOM0~<0X;qv;=NWNyZy=}3$I1-=J%3IICziTR z^E8ge3F7&UYU8-B;aaBLyo~JSJZb-SthjzRop3RuxU0PxluxVEgqs}D6g!~wd)v?V z>T~&No8cJ*Io8Z9*~zCFgkxfqNr*>u(gh#p${2fxlL}t+We!Fi=EqmAf}p8>8p`}R zJI|S}wNjTjvzqvvJq%((n?4Pjf4(gi=IToKv)3}3m)`kkjvuhMRT>Y5My+$dK%9#$ z>~K;~#k`utPo_DqlqEoyg9Tk;o8+*Vy%Z{WqCelDEw0He=me~1Ly4MFL1t=d5LB>x z0Y5%lq|VG_`$ICc58Pinxy1x0dhtM|?-?JS!CfLrGYK@}^`WGR* z>UZMPW;>^_>|(PL^lw&!C9ggPA``&l*Y7r0U@HYA-bW07%%G0x8k=bRPztpU(y}MX zW8=OHdz7C$*ot96b!gW-h}5psc+xI(zY4EfE+*Mg4ho}<0i8V}j?pP>7 z@snmR^SlDA>w&*>jXZmsO`DYW@o+thf_E!O_%^iH+-n|kpIQbCxTU|0+J5 z?ambH&ME%T->&^GQ2s43D?(w@Q8XNuw+`UDF_v?_l#NqEb`lm@r^r}4lS{4w$0aV! zn+Br&D)>_eac?naEi@k$+GtO2>rYD~K+&%15dRQB8y?_Pl@To@u|wA;HDaIXHUz1) zy+w&u2qKRbX$r5QY;hxnuA&N5l_i)PL~9hE7LPm5*sXn@xFIAER9h9x*`MKCer*Tf zF#Gg!oO=WzGe($i&D7Wey%iuDZt6}iJnN}7KAm!wmA#J7@dq`p80cn(1$D!f2;4MO zk9@9!bC1K6Y8V=ZHO%%nVO_}9A>DhxQi0>xE<@M{XZXP(oGCXhY zy2hOkN_qR=NtCSEASWP|TU}_x_;`XB7f3YG_@5QFhQ(^gVx?u6t>8%gg5lj|Jdc<) ze%CI!bIgaT5vjEVwt!EepiMS5G2#&W2N$05NYCJ^P2K#p7*$-*-2iaBT*hOx4@t&M z?Ea3!FD8IM|5}#@T~vn>!T&>|Xu_3&Njj!1)u5$%FFcCi{ZL{ja`W?c zL##IP_j4n!(J*U)`%@k>Nf#6qu?x<$CBn4-JspBS21C6I9ohKoB;Ic|sTeUaRJyM5J(qH0_Y=Ov=!(dJkCn z$SB*LAg%oU94v2e0x1C{;ZmuyKqg`;UR^`=A^guZzJ~h^l}W|{bH#qIRzx}orC3<;^3cA9!i+R94a(Rq+- zyJg1mKa7#YsRBGB&SUq57_+AEF`k(N%`cE$H{1BHX7jWzoBR9jL=T*PwBdi2wh@56 zsvXo!sWBaeoC2H^bnzwQ>E9?D#TjgfdaB^bgNgx<#{}>ej{J`Le?W8Kr>xu28<7>% zKE;rxFE6+_Vtx5F#*aj%@7=B1xs_pHFm2Y@{AiEF0TV39rOSFo5do#K8x?i!af|N? zgZz}*wmUrZPfa{?>9`fMHUQki=Bk(iBErd)4AnjqXaqZb1z_X$Ojqn?Q!o9&>}tPDKF( zlTuVdbECvo;Fx&Pf$q-1(a0&qnA(6}@nvx_#Igr}zI$QyVadJs#^5#{*53yjq_~8d z{xR;S4ifld6%}2svv~lq4^pOjh);6uF9jq`=k=G0F%q(|axu5m-19$%6DDm8-6Z1+ zc!C^#GQtD8a_gVT98pq`RIRK!ZuBHo>HS>VJ3HiYrlMekXa!8h@rF%RJHpwhQV!vt z&yH$24A`c9EVUpPdOFY+*X24S1Y<#DNyA@YM*-UT(?o0~6FH7=Md`%|GfcS=2LIPRNa@me6xHo=3rBkeP;eBw$71#VuNWM!=|D}$c{(W?X%C;&@ z>hAMLsdvH321iEe_02;e?ef}%vcl*0QFp@)$%2iRbKwFIwe#l=RZTUV&AcI}1{;=u zjNuE01mGJkg zBX94M2WA+NG9Pv6Bg+jZz@-U5-p8xoeQ{q*yohowS-T=Pjboe&|E@GB!+cxlo9&gu z;=nl#DZMez=UIfyi9MwB-*7r$?vsRBXd=`jMY9MS%*SN$I-jmVCA5vygbM)_)G-3A7-%L;1JLn#|DA zwrZZ#smWeK_fgC^zvtLW$Bf4ANA5}D2kfv4e3ID*D*#cmX|NHolwo;QiZBCXSFMRq?bin_Q@^s*+Z%;7t36}UwX*Va-_DT& zjnycCCNS$%BVceFF?JRMH3w^^R`ZTCP)R3O=`7Az)AAyZCf6k$%}M`=WiI&>BOJ+V7uvPxNXVt!D2kWiwrQXE)XDXDD? zKZd98@eFpjHmjGopTTPbFsI2zXp}0sM$`FX*k-PcP*pos@w(;wg@M)pF%t!tn>CvC5vzkXrz=jZy92Yx?=|#Vi3|K>BC% zh1!q>u%vUJ!mlo)JVY{psBW&BTs8^arYmJfm^RbhmEHbvPWWt)zA@M}fQ1v;K_3e+ zWiNsN^9al4LJj^5P$VU zG;Z^$gEc(GTzw$}#=DUa)}#EBv+s=KDZcE}fsdL#>o#Jut-WI#E@L5AJ$%A^Ix`!m zU4==^QxW40h^6fSpxGS#Lc9Y6Y!p{_t4TjwmU< zXG>^W?p9CL%ENa6;&hw*R(MTwo=sipw|1Uy);I)B?CTzphhuM zoYy5+LJJC+2<)tXx&4&ge*=j>FyPjQD@TC;2&OkHX4u56IRGkFgKs#t#D+^^$Ve9# z?wk4oc5d*sE`HaBzmJdJxg%nROKw`(wcQHUnNugd74`t|*vI}KY3G0?h>`@rwr$(C zZQHhO+qQe!wr$(CZF~P6_7w3dGb=(2NcF|Ao<2T(`=kbSoI)z0V4_$JMm$!zZs{h5 z7gN7G3-u`ufk2;xoyxgbVPIV&p!>VZB)1Em%CWIS zI1=QJmm6i{HsR0pOs0^lDpVk$+k0|Z-{4Dk?9ilyB|5>EKJL95LuZDm*L{t-@8?4A zYH>{m0tyw8py`Xof%ZIRJap@7zZHEI5RYfJh`5^BSuM^`kimM(4>kU0KDW~AGXvbP zu~Dwl`TqDHfnavS>Z2yG`a_+M`gcs&>C}>JKhw6f4MzIW?Od(?Q0ZKM*gaBHLGvNL z*v9B|$vzBXseRFty&}F^@>c`imkYI$dWd)ejLm3IyEO>m(wurtz;t1V1uc7PMHuTh zxzEh`bInbsALObgY%@1&lN*k(*otrC&5w^`>%F%Xs`}0 zX>0O=OrO$*67mxMB#vVLS3DO(bL>;4Ns0W+K{AG_5iN z;Mhr_y$~S`m>Utiz4bS(EVyGS;H1enX?k3b19^w18UJt7#G1UpX?zIOeya+iehRPn-UQ>mp%CD`)5tID0}*~(Si1d*iS;L_>5W(pA?Ss~*P$ct z%Z>JMFD%$|+XN27inpW=(VL`}jFjW4`MiIhy zn9rB_Z0bs>fY$34`$ySKyG^hWzNv+HbL#V&jYt%aB3P-CZ34GY4602S@vtG2g5#~k zK(fP2nly1}7V%h4oo=*rb*h^tw&ClWw9{4~=dSQ@ow*0RuOBZlkw=OajmwV5d&>LNCU2(H${D&M#P?v_5#c8;{FndVGuo7E@KC(dc|3)BvbI zhY>M9Pp^|nClMN8nL1?^%_ zS+uVQXRIOW#=NB}ce|h1g{HuPg3=cfG4qvNJn&M||>>9v&emxNxX!)w0Rde;eGQwrxI(;wW-_^q$ zB=kIw0yr_^6yfrp=sS@=tc{qm_`5Ui{vOG6etTB~&GvUuRPC5qO*Nn>F zGI)5<%cQ}i&HwFj^~(#LzjUDY`#~1(lutOTC9>dLOE<0Zd!XvsARlquL;H@$milxg zlbm>R%$%G|J%V-)$xC_pa+Y*v`+8QFZvB%?3_CHLz0Z(eTIBWz*@c7Y_>o6@A)dkE z3wKrB#*fm1qe7_Tkri1@!WcT)q7Y8k-4M;BWnl%lqt_n#Y*TwSb~5$j?ujVQ_7df) z1M^Sk*8K-VmDb|OyW;x2MDGB8S&3=%xX1X&tWI_1lvwZZtL2|JgS97NJ|6cgdQv(J zh3(Dq$*`9SR3Y()d$0M==sSBQ=wjH=zm0u#U*aG@uEDS{HSQI1)-K+I8FlN2B+aiI zezzdVxAnp_$qCs$Flh2PfWh(kAh|@~U-_i;!1gqT&%*bP4J>?RexvO+h_?Pu`~#S+ zm+2sMXjHLqZ*d)(Q)+bgUrvjQ)JDEoy?FbWt1QuwceqS>t`zIide?!UhERM9YTuH+F+xtQ3f*N7%^22 zXh1$da@HHyJ!5^uiN(Cmlzin0(V)HYg67=mO`Y3c0yME}AI5&oNscFd0&jI$3Z9n< z(~-ob+V4Coejdkai9Gw4ky=Vd!d@s)ot-8a<5M3=n2lavA)-H}aoY_4WX$5j7t`Vk zHmfhWq<6Gxx1v7xjEB=c+zuW^a&102y6|LorS7JTAgyZ?!5dhYxKiu-B&aCw42V+7 z{)d@WFxpkg`HL|6aq51 zlqLzmjEayOTDOW15`BSc7~Y!xC(}a9DiZYrTt58~sc)^gu;tSh>MfSvN}o}}D*m+( zve5EY%^zh8H8A!&z!*)XaLuf>m#QW5aS~{9bR4R_AbA-ucJ%vgEfdm%h^_NZnFESX zwLx7~!qdOyQpS$cQx z&1L7Kf^+Ec)4)1O-oR8m_YXcoi)Pc9IYY-7Hr|~iyg~BN_zB1fcT99RDen&k!WMLp z6f{bIopAKYHl#<#i5}TeOY1@La_EW3yJrBS>2y1;0|g2h4kN4zwM_Yf32D|4g>Kx+ zKU2TwgKOAjU909_xsa!ED~%hWQedKSy9 z;uvD{p1u2kJswaJcy%4PB~HhhOSY|@bw`98vqqVW&giWtvm@`+N!;J-c;?Ivlrs7K z>&L^HcHEpuSK*z#$>B2TPj%UQ7&M?MF2J0Q0Ex+kLnEosUPto8?pD?lhOLBIgFuXO zYbvVL?gJ)Z?xveNYRY8Ox{7j@0?MlQa-I5@{ELJ%`?{z{z1(0s>kH|^I~XF~v`AUW1>CC+97RygOQ-Kg@;DCgMsa;G zB}*j*U9Za@u=w209e_{AErm}bgE7nU3|ybwpu%NXc~ClL;c=!DyldB#{t#Ql34E%@ z)u_!Bv(%7B4#ZOD4Xr{fP9OO`>3*Sa`tQ`bJA0ewcCCibS_ulCM=w4SwrL3o-Wue9 zolMwKm{!%OXei)tPGygecA1$h{Dr(z(BjP|`gaZ(%Vzx>VqTD);o=PhFeR*F%SVcS zdKla5hl~OHO-&mYo&_2w3%!BnWiFolm9+9<0$h)sPQooZXYwjA31WJXUYkWyg4{cm zFJuqZk_l4n=hm`T%N|V~Ol`)P)~l+icDwn5R5}jfJuMmYN^^vfi}0oT1*o>lWgZbiMB%Ywn+e5EBHum6_OSn%-M3eEoDLP%@}eVU+3Hs;^r4($~J zF%SJ^n26EtMtF>svfWf!;E7zHp$i@kYYruaEgT;X_)*fc518njn%VxKG7`v>K(}31 zk^e>H=%6?JSlbb4@0N*4Z}m2|mFS7sNi8J;s;`{B$rsYWzJ6z9-WXR&PSU6C`Id?3 zAQd?Yu#kjB?SQI%4gD7G5a%;7xWIf1VheGG3F{3I5XysN^NcIj`g~!b4^CFXus3cM zPK25>Ope(boLeg?)~@23${d_dZb7Ee4`mbEDNg`nE3s6tf>!*gg@@ZHLx=-{MnLagGrDq-4@hwPS{MtMa z0r*jy{5gAJjtnny5KO^3U~{0LpKTIpNu^T5mNPSi@I~;%nH^q@KK%;?a#{Xsr%83Z@{619vK4X^d;T0gOtCUw&YhPUbty0GXX-qxc-q4Y{x5$93k%If%I$% zXrbjwvVIFKAf0zOk48$)GDP6yB3bz8qs%sG;C=OI5O|5@-dh!-?7*6r_6+9nW&Qha z)l?tYq&2}Dl`UZoJ!BU*S}eMbnz#)sN0@vW5o<V>3Bbl0XzY)kJ^A4c>epCn6S#+{j6Ntf{oiR`AS(g$Rf>}?kn$0%+URw&ft*8CJv*)7(%cA`Eu5rA!x_@ zT^5U&i~H~*ONis$x79#)4_Qt7j8|vTTXZG1;w1*j8Zq@sx&THB8F<&lPw}?{qu%`9 z)FHB^u#j1bRuEs3mnc7^D*CamOE;l)xVmrF79*06^E%VT4ZE6Ig}azp-<>REaWY%u zL8H!YyKbZ6&kRahuHpH> z?5&2X*K0#>6clah2yDCOY+63IENL?EVErl({CuK}UaFP~JT7J}+TB5DJG_{qOOK4& zOTtsqffR9<4Y`KE!1N!;lI_WRQ7Gtmen(Mol;{!AfuDM1ZN6F zi8R?FWwHn2I&t6}+3JoG{mRjX^Fl%+Url++I%ilafO*8truwiM=@Dm=<#q*-2Q0mn zx6C>;NP5n|wIeXxtnJ-*>A_f5)EU< zv?H~gI;J2wavt!dU3M!8d;6_H-wo=C7RwNS`5M&?dtH=+R$=6+tL&RKh!vf2b@(PS zyBHdU)Tgk3X?e=&>7?2{GQOxMu-R=)U4wG)%kT;SfN=^_-cU|dF3B>^E+Ix$u+UFb zH0DUx9;EdznQP!vZR8K*aC5-w+>4^lA11GPRovb6;`gzM$11H=PdvFn)D z(rs`h0EHda4(!Pr+ZcVcDcXzpnT&NvsN^`Dib0TjJ!qwO0JuLy5z3!5z`cB#SL`mzB#c;AMitUtV93IITF-cUCSN6gQsD$@@Lb$- zw!*?J+0Q7k0+#d@8Oo&^Y&!koPFfEUx0%U7he`Qgiw;`@X zg)ZghK)Ly;fo>U?j;qKOP>sFszDxI;LrET4QIC#CWtu7iNU(4~MrYOH>X=uz@0FS- zy&6DuI9PhNTgZnM;~GLF?aV{JyIoUSCATPATvfI|>t^v<$(jXHc~Sua?H9s_kXREe za5i*ZComh_(Cftya+$XqX>EG?R(#XYS9kW#TWB@QBk(+kgv>vl)69~CUD6(eGRL(P|XIh{3AFo(T+>*Wuuq*dacS;%442r*y$H z@k>C8l>P1vckCiAbNinQtVJdfgxf0w*q&@LcbhWBZsL7FJ;o+mLfFLQaioMO-8(Bl zsQ8OlW)eZdgxEX-!8b@giN{G%@*hg~NTG$L)SZ|0u7!LxoNLQep^|I_pq$uq$um^2 zK($$?`7d!UB^(R=T(SYUXd?2*cY1}M_Veb>TC_04zP9W={|YEy4C@=-h3?RdPhbAP zzt%oki<|NiEnV(CB+We3UFe~4^9{B*YzKLOD!=*w;5Om>Ou~P6u;-641S9(PK zyeX&2mak<}vfb#hhBm2_;~f@n5#BH#Y_vXsurdAO~%}gZ#mx(9I@xbWe zKh=Tf92!ZlS9ijbOd}&3nEsN*x_;LD@2;}fR$ z@QSihPcJ8OmwaLy+e7sE6Ey@sG!C~81wXm+sPnr(n@Ts-ef_sE!S+<-l3g2eJCWu0 zC(yvVktd;tHYpEaXV|XsmAm*$|;yz7`8( zZMl=a`eZuUGIGK?13inzy0wqy9@%T`|8(9PV z&Sl{|34-}xpK6&)>R-WA(CR*ICfPMjPVo*D!`SOyL#o?D>7*1Yb5a=I!N;xC&|(e@ zBCF^Sw`1ql0oJTj;WOY+vGq)nW~wsiiwhNiN~24@THSb*{CWcFPO~=?>tc4={%rP= zL7~ym#LzDHHGbx;)Zxznt9%F&Ya1Ji$>r3pxMgzsnvIFiyAhMqCt^fOiS7sFSXMIs z&o)O)U5n$|18Uq7bG{0cg$&40!F@wgP&tDyT5fsqJ2R_)o6_>B9est;ibrn?ZhVz;}_+Go@*=JHngmz=sL|spFTA)iB;~^7XKhcMyJK!8sP{iY{Ebg};EGpmLErOR7 z+X;Ia%p`{yb|HF!DeBtSY=%XY&gwFjH=VizYsk*7d63X?_csE`LQ}E&S>?2P8MCE1wZ?;8?#_*N#x~>vJv4GXOiV)^cwaC3DXb z@U&ZUmN@Iaf4|dX;^d7aG1WEKo3ke+Lg4(nlv}ox2@GBFx<^ti_ziDWF&-}UoStzL zR*c57g{O))durq%a?ftNn-xXfw5QqcDcZGe8K-o$sj!IUljD zkt^+UagTS>NlJxZjGPS)g*1c2^I9u=OitOlEtHaFkVz+?z^0{e@83KUg7^cQamj@P zEU3OMs@@wz&!Mg~4p_1oclkpRb8Z?=4V^ZE&zuCUCBxP5$%!-0WgXc@pX05iXsdD#x)r}Cd?%ws#C5@v zRB#WGMC{nx|1m7{x2R$*_9F@)rQ!FRO-$K}wuZ<3c#Ej z+ELecilyIUZ!`G={lQgUd_@ZXRmG`qwWx`T_5=J__H{4j?~$&gF5=jZ&p z|Ao3rsL1)dy@yX4ELEfaQZ51JYV`02l{*tWu-QmBjvOL2^>p>aDk4>%fCl>^KuR

=LPIioApTNJIRfQmQs0OY*EG+2zZh&y!|KKllgaq1i+W7o6uho1m+ zhjdsvi!C>Xm(oSk!9ya@G7p*?@3-8M7f?%;`KW35z|JF4X>RLM_3%N%$pw~&Pbvy<^2*6R%UDuB zsz}jGu;Z1bEu{?%m_;SJa0Cvm&+i>2aQrk*f?2;N^5xF z4Mk#Rk|>MF$P|T7GUn0hyKLDLc6e38rEvN85)bq)&fzZrq=wMegW%lB>$UbGUzN2P zvxhiCMONhv{5pFCx~y}43&^=H;rQ@0Y@x;+nZrVMaDCy56rV(}r;X%R6{9QgNfwer zC(I%Q2SvjzL70rziri0FntZH4uWq-sR!ZD}oO$QCmqZqMA-a}Y#-YSFU#N{h8g?*9 z8kBr#GK~wXQUL_G%$Fhu=8{sMx_J9g8~_%A9z~(zA$ICv&G~?jxd1$ko?#S5oSTW! zM5!6$m~911ZbqP=I~kC72VR&s!32EQmXEiAIF;_s1XNnS+cN=_5jIAH*EiVQ(neCB z8!6J@)sySR4E$PGI?An-rxha@>rB({@9T`7+rk{BPZ2-UsT%2&;7d0wS_k}xsR}ZT zwpspXRl(O-wg?(O=`ln#23H3x52^D(O*6^nOpdU9DD=G=VSgvChOlsRFCJf*PvBJt zuS`GT>M%Xna#!>V8$c~?7Of`sAf5_4mo2-1)|2S*vLE&<<)d%9`^KJ3(>T?oq={mG zt?=@ka5axK6f!$;dU#L*OTKnoQ2lfyl3b*+$M}&FPQjfN74FguTA$$m<^O*^54L|O zjd$8z2vU>A55O6ahhU-(l=D^GTG#+}_iC_k5f0aL&tCkAyuJ3NOwK*cvwl+Jr%bBx zL^)Yy!#8OnIwSMGK0}LDU8L&96{W^cYc-F?e%gBv&U8WSv$>A=9gS86CkH%e`P#%8 z62~>DC$CI7u8(uKsA2C9hb4+DyH#h|iY^EuZR714OTZNy2yoC10J3dLn-mT8NyZ=( zuFx5UBIY(9Z{@Z1phHX_G#I!-bRd&#V*eC$zh7g4N;;i*4jK zxk;PZADyqDFa-;r#NR?5WdiJr?C+6qjK1xR-YKzg3+=Tz#@;ZSAgkciY(S~ z6ydlvG^fg$27Z35XETkPXZ|WTtN9_Ki&(I_qfKMMD<0kf%aL|f+pRvj%~7*iomlrX zLiVFiHN#Gnf#-Yzh<(Q|!nz92GYDgyAi7s;ctpF~4rAX3pu5WeIR865t|}F`Tkrn5 z0}4f|#!0kPNxK#TQl<3++^0-(%YzHeNH>iK=QqAne3Ow}4*$v$B>k3#^E~+B`I82A z5@X{(ZzT;g-Wp%DudyM{8@1_(9<|mw#sp$<_NQ$1#7V*tWfu>@*{3lH~ z4`kRmnudL8C_?X1U3XcInKvw`)4uE)p*(j?p+ml}LbsEPCCVCY2nyCxlW0jY!{$9B z9j%S#^rRLePsMJqSyJlUlQ4dtC$pW;3t_Krr+l9xd+;nAbX~cLcl&Ynz8!rnh@?)2 z2LkOFv{fQ-|96y`1)T+iMDDXgST}jT2rGZkc-TDV#SSI!<9C)p=mpD9LtxnsM?mq{ zruinv_`7{W<~p7^x1vlk5^~kbp{O`Z)za`>^i3S|SEfaV<=HWW4E=`|y)v^&1dz77 zSgD?*v$BPG3jb78S;_r_7Xd3qES*KA$0XK|W}>i+p#$uO$B|f%?(x`b(bdHMfu`8k z-iIn2Nj4BxV&XGih#4}ZbLeRN%o;pgznTXJcYDs$c`yA*7(;2$X>Y&ZYgWtYy`nBc zYEHzUNK4t2T;HjSp!gh?84T2+Ne`G)?!p43kr)h5&=dLN4_e@F2cmP#zwO0!UL8JrD59EHCtMTyExnpzzm!Z8X20KDRv#wH{cv(I4PO!4JId=zhL6^q(!?4x=8odxATl z>s$lw)y{r866h^I_h8w!zFKQ6J#58GwZL*hn}Z+{DGje|jqiKn6s1Sn*t zxg+1wpo$PfXtjab?(-$laODZE<`j2~67w&j5=y-#9L~gAW^Z&FMt}6tC2ALN`t@{$ zK0TVzGz7Yp?T_E}xCG%;)UR`-Ra6jep&%sJ7DNy0A7c2FO zcWpu};4M*}sQr_IV%#MGTJ7xH!@P2q%R%{vd)UIr z%@|)%-~<2}v!{Ec{Kea7nm%wd^ovqIH;w;XQ`{12|JkXH4BS)6&RPhvaf>^=+uIQR zs5ZSL8eg%T4fQ-tfGRLMyPr$**B99(#e6jl-jcLg2jqC_s@Q? zmTugLS{4_H)%B~Y#tZjP6a>qs^mWA`Xvx&RaV``c7T z#pe^U+Xs+G0qP|IHJL}K(%R5Keq<6pkFD{)2V1tH;TSP&*;4vwg;B=$YOcN_JtI=1 zl+1qzBvQdY`Fg;%LEh6w?Wd9n$KOW57QFQrY<^8%l8;8oL87Crp!FbcPz&d7N?mO2 z=%;bW+mF@DO(6BGw6Nmt*jK+y5T0N;{-&XvI&IV-0I&Tw%g}4lfZ2r8exAS6S6r@C z*V@@7*PIjEPam6*HL3H=7Tv4gi^ZFYJ5?Dc(P*)ZP$3&Ug`~v$2tIRlb9R3DRQ)9w zdwngZXGSDQ{L}f-QM6T*ROa2f5<3=4H1?WG>A+60ymR^HmwdX8WCo{!SWtm5Hb5u> zQYi7E@GWk(T&~^4%Q`0KgxkX&z_?16v}7apIDnvVanu2qm@?2^IR)6)mG@UICR>EL z^ehe+a-8@mQp4D{PzA}A&_8_GOSfgFa;Ug+K|Xf3XJ(m!pTxJYNo}CzQr9NSl?k-8 zVj{FFUgUeJ3$o7ztzumdjwZ1Q3K2iZOQv|u?iI){0Oibg2kGF1YZcY#R)635p;}qD zK9hJ)MGl!q(h3vSuz6jFa*H*+AS3(FgWv9I8<2j(`dy~vN?3Q5VM);q^13L|K$2#< zFh6GhFaL2gp0TfdLNm-7%BsT-SWl~o(oKnWV#&b+T5)A~67qpi9wj10?br%?tU6F~ zni&%q1@ck2?|e2? zzo*rtt41DfVw?({qh?+{X~e4}BZV45MR6qn3V<_YJGF@17Vt;_ZkK6y6`6ke+M zBmH5d={?cvvz70>!^r|s(uPXK5NQq^>ToXoY7w4`c&AsT+VJ(In_P5u;zjUon%9GUjv{^K@-ZBTI5< zFo095@FAwt!Z1>uN#ycgb!`mG38~;DHLe}i&9aQJ+fvd2S?JZH<*`yj#T7c|@^^30 zuK)ZUJavAQb6O7DIy34# iBCwT~|!6zCd9qmarLGC6es5rkNiDdwBhnfc--uM8! zFHkmT2l(C(tC4?MLyHDb-zRr|GBWqx`@r0M@PB1xYtrc`zM@y{ z4^l`L%ZmRT_u?ptu*nyDB7v)(e*v2PRizIrZt9<>9PvT>Xqvx<0%J-a`4x^%=h;y- z(z9VMFU}rASAq?)i$$h4q%`V{$a|d^$o$|`Pi@S$&cFn?M1x6I?gf~t%1nB21G^D& zu%4G?%#M({Zn36g{8n;=Ft<8ulZ(TOmt8j(Yqqj#CEc=s&m!TNY zNf3)MApdO({mNY<5ioW)nv4!MDmr!K#CuxP*<_ls#B*D*3$s?V_Sp-bdocrJb-;vh zMeeJv19|7lxhV6C+Y*E^n)_`DKk(USLZ)_mBy-Z8KjcJC$3SP#=d|>e&M18sHk<6J z&R{o{_bjnbfs^{45C3K#Bed$3OYn*g=iv7od;z^D74PhNjd6oIQF>f*VGYP&C$pRt zMe22>sO8boDWbQ&d!s)|4(am!jX`u4(-G^UAikjG%I6GuHZd1LK1_#wu)H>72yzHC zvYQfn6j%)RU=HxFM%b6DU?QMUi=r1m6FqPWiBxnZ|CR#NJyLX~-Ku^3S1&|Nxl3*& z{YtzutHkZ8mvTY_ilI}0O^ARoO7B8kh%ExN&hDI#7{M~UY|n-OdwpUKi2JYR68%(a zYd@mLVyrFXWz+){v_HtMBw^911xesJ_f z8wqu-5=DRjZsvh$7t}lc=u4aq^#)TS>u@9B93}X}JPA?Jaa+`2MdEpSV%DrU(1L(m3R#~>i?^3u))2sXwHds|)J-JLCAhU^rkKctBKRngo|r{l zqc|N+uObi?&vSJweo zxi)FMKedUd_XF{l%X?)00z(2!N>?FC8;2nJVQotN@1>B-nF^Jr>W3JF=sC|tuh2&IZho$_ zT#@g=?2300arzpriHzf_%Q~}v-XRtYJ!yZd9PehOh?w2-o0>QBjqsaVr%k9C`6^to zTVkm-ydIa`Xgeh#1OCh|Ti|x7kmsvwiPbx##zF%t$b?uTO1v6cHEJDPEqlXWS3_iQ zpnD4XMaJ=N62qXGi;;U(hv=l|t&4b?LqtHMK{S;?g15kZ@Uv=4iKgS93M`-`P@wOI zjuhP{?IPQH!k(#cm3T(46q6r81xttqFH`dfFriU$TwQ-wUu^OnhO61*OhvFWLr6hD zmAFb}3KXj$uhtl8(aFg_EI|u@g-(|d2YJl_d<_H_D3TnngP7)Gs;ov6D_JlgVsjt9UWR_}(!n*QQ>ILQckkHk zs#oF!Vb7VJDc&#MNS%WY&*(#z_qt={%6c`)oG|s-t6hAtms!31^CXjDAJgpvG(s48 ztY;^U|EfzP-j=}LyZipH`&ndZN+vhsvQ(`)K530cfD5jf>lJQ+&&a$1m% z4TgvB&d1!P8X8zl>AU&=FaIk@t^yoF4-I}`;v{6hZoJ!PGzZtZ@SG|VjJs9KVWAyl z7$6?KVlc}XVEm#qSsmWzX*%K3Wed%-o zSb$w&O^sa)uGgF%lpE9K;IW;-wXv)Fcrdmuh>Y(P4cooZ0%pSnP zuag+@MoEo|Noy?%yOYchTZS+nzeVsCG*L@x^a3JqH5$oa@s>8Hv7YnsWB6YB%ZcLD z*P$EE@|$q5$>5xxn94;fKyDu~oVTgEF}lnxzJ96#Tkc)i7&xu&4$U_ubx9<>`{;zI zw3*`HudGaX&AeLT3Z!X54Gnr=B;6>4ktK?T?pUF_Z=INB4?CGnIbyrx3R!T1tF2$w z9R;7mhXA{0j^eFsCN`0o`!jU#lhneXO`dy?M~y$L9?9HHyC}w~2mZaA6S(?{h$biy z8hO}Pf`ZP`5B9s`+*p_1GazuTLF1$5Ju-&&?w*cCdeUb2na4T7f$OZ`pMx!V2SS}7 zk50Av9Sq=<`bnzdA~Ub-`qlkgvZTj;u-avL9(4Y6Eh+NBmyK|>-`bYX z?Gk9`)>%VjfY_3wv)PWN%$X^S3_lP@{!I<3I@RM*AhGAPfv>XO2^0C^_X~LW1qyJn zfxgeA3@a=B=#wD(x87@$e-e6y8NyqKxDdqbTGRWJ&A+`g)#g$$@qcB98%T+x>WhA%-5lYGLyY z%%?Y|bdS$V8CLf47V4SBvp5 z#P6B%#e^fqWbn&!-+HqS&beriN9-<#p!hz|#Y+ie9uFlvnvGD|n#nn{aZ4OSXBQuk z31Tl$(F*-h=b(xApBC8xAXos3^p9CU`MOe$7P#0ML8sOz3zFYEb03m7~o>#Qw<$*Pl4fj%#mjP)A|UEJS4K7K+(5h14)*)3ZRQ)rp2!c!xlaJ=%>g9@fw{7q5u z?$fwdON12X`j+Pi;Zhnfa-YPhZxYEQn|!+$2HbrFObr$RHKC>4gp=k?)FA%HkQ4xh zXt31>6^I#An|g$q&(7UHPJB^^nMYm0DM|C@BK>dc!KL8xr4d4oSuk0Vy`@G`2@lTe zUmOOux*;O3G}!1t9JKnXXb2qZFXCzJfGec{(~s{#M;8FML@_DZbwnX!*a*N zHie&NP=f7?wgm))E&92#=g2gpx7D~lFxV$mF8zUDJ)(v~(^Z?Te3@BTO;{eCf(LbI zKD0>5-Iu?PMwbp=4+b=(g;Fb{lUF>hKgc=Re|=JFUxdq#rItB~>mM3Z@J8;=y}BFO z)PnwD}d|SEh1bR&76qQkbR4Z9pjRZ@s}B3q{32A0u&LGKN7EL z#;s)NJ{BcCsT z7|#O%d1xgeq4jO?t|FPODlu}CClbn#nm!FHeY!4WV`ju{kUbp4pEkZxH&Q6c7sXH- zE^wQ4c-7H8vt}X;y2yR5y+NAQ7`{*w1jY|Oq9djooq6c7f*6)ah;|3!(xxY<*8PE~ zoLUHb`Q+i%_0S`5Yes%#CDnVQG+=k?>(2k%|9kz8xR9#@$7gUh+!o6})U8Ox*^`(> z??8LOG>}?W?2C=s0DS}njipF*1T^0HtK#ub&N4`W>#|+o_juqp{sbCnm@=28{R?$t zz8{}6B~P&vL8VL{d>By^3iWl%s+420kA+9YWZ--Hpu@oRaX*KU;9MDgGt(=xc{D?W z`1}~tP7!&8*gJV;ctrgCQ0*1(Ovl;RDQ`q1>#Y>TSeyB{jf<^}!z`HI<=3Hj7b|bT zUt6VJWj_N9w-bwl^@B|^E-_bLp(Th@vGG+8*t8=@)_yQw85B~ryjKmj!3J?tsIX0a zL&Cy|+I(lJ7|pr@DAdQ;kC$5v)|nc3jND#GOvBfjO%+$fmn8Dv{Fy|by|b(Uw4!fM z$k{w_zJFTk54ybJ8|hZY6l=l$zNA%IFr9a*Db4%-)=ccCfNpMbYDshSD?2)n3x%hN zDln2;O^6SiOPs!?NP_Tq_PQ)aMX2uP)0<0ue_od{sajk_)O+RWBA8DvKALbCtzjdy zvPw}`Q{JT|#FYEpQI9dAFtk`gcO+|8;PK)buxwCStMEVhTIR6Qh#c@HTf&W-KY;y+ zokRhN9;Cd@fX1~LWxm9)cv0mVh&UU3fU-YS-M-V&cBLIuB*0>pAQe#oZC=`3t-l1p z1gX~Bzxrr@1Bv45B2_B$dwsA`oGQ_5;LF>;%i zFLZ*nx9SaRdO9<4B7}(eZ^1k6mCz;>FU5s2H{TNSqs{Fgef&rrUB!RkhQ@2={#gvl zMTkd%o8{QO7}CQDlE<2S2z9A9D`SR9)v;w&=hsW^Dao1jLA}AhHE3D)3>FO=YbgU* z!@}tPbmKugb=s5goB9JNV0$&}wdG~P^)52bj{gekO1bIaMRWXwS5O;-4$XgqHKYcW zF=+;S{zuw5V2PqFOR#O*w$0PFZQHhO+qP}nwr$(p|Nb$&LF^T4S7k*CDSW&rS$WS^ z6lU0^P?2{%M z7Cd=6Yr#p*5ym-hUUfg0S4Qh`)KRV`y^TCHuyiL)sT#}KRECy0Cwt@(Y!M^~%es^ORYXajtry zk<+hgHy>v{dFQTo$`)h6A{{DV>O#=*+H~|G}8k)qzX)(5#`HE!#$kqN0RfW@|@>(dz^{vgci2 zv?LtUYq(igkhNq|r8u=Xmj#5ORw1^6xaU&Ju|MF0R83%{9sP8duy8<}K;WA84L!}L0%rR~o!aNk> zOV7GltM zwn^>2MOlVNg^H(&aHi4xI3jT=X^7|^d69+ z63$tW^_&(BrV*9P%%HbNuHI!Ea3DV!1NuPKfu`RaXC06qII#t_VH`k*=;jf;BZw&5 zkGTKPUYP!=5H3ZwSAF1(w9QDlw&8OU7aruh5IaoaMni3N2kfwSi`Ay$i5_uh>P-F2 z3fvr%BU}f60>aVPiPn|fg{DKT(uz)bVrmbSPR0*ot{{q{Rc(oP>7*iRdnKT4FR(Q;C zGjNLmx^-}D*lHrUI0)*Oc{7S2;TW;*3Z|mVG5D-xX%+JUjz>iEy~6m_Tl$x_a6xZ{ zV4K0i>%>}0lk)W_W)WGQ8&|??G26{t>!3y8nX*8d(4Aow|lHbShK@<=X2GIhap zqs^d3S+^^`Li6O3qx$P5UCb8@R!Lkl1?_NW@%=5%qD%D3&HJe*b#;`rhi(@1UyJO> z886KozT10cG4gBcV{ow^3E+`*hg9_-Z8rYb|G!=(b6W*8{Xf=6&HE{_+1--B>O$1F zqxZ-MU?PE1uL*sG0Z0&)x*=MHI#E!XOI6W;$EUiO!O!I9+!U_n&oDiNk!1n22uPD2 zKbu(ibFBKF(_TFyvmE{PVLuISA#M)U^Q2arQ(^khizCIvmbYsrZBsP`_bqvPw%WBj zdix|G$~0|Dm*2kX@TKemkiJc;1l_rguCHtEj|3;hi03t84XPpE{ayQ^{hzGl?y>K6 zOb{1Bg&w|%Z9doK7v-V)J9r&2Wpb~ff#`~1`Nt+>Ufh+63dBct&M z=ro_jDl#HEOXcvS zSfgz(ko;_cQ@%;H%S!QAF?;l?FM;^TW97mvD>Vw}*v;)9s=!#?4`aznH8?YN=LfdnIaj-8c~RLxhCCBo&f6@yDBhoV9!*K?C7 zHzf%FU}E0pPEb7IcZA+MLQ$kPV}grH20`5V!bx^W2UvCSUnQ>~g}(vUE(n)uXe zOcLyqTQ?xCNbnd}PDi#m+jCa`<+vPDs41;U%A6^otz<79^8h+GFA&!Xmc9U=N8&qi zy@W#u*TJC`!jexRXzpOgNh7i^WLNff8Qf+{H9spwZuNBWhOmXDnyyl&HUKD<_js+s zKe?RR7@Vah8B(UkuK}ZvS#|imzT$vpoSH$Wu#^JZ#oz}NKtN+MLv`O%h5InBt;roE_caCzCUPbLHCW31CCbg9i;9HVp)bc5Qs_I6L`Jy0Vz|Xy+OfE7nJMgUh?=ydPG$E_YZ%$?=MWCqr*Gmu^rP1me+> zAWH(t!uDT@4`RL&nwN#&D|99CukK!Ki!7adcSEZ`bk&~RiK7t~Y!XY{rBvyOj-9jfS{(++?N#PeH|F$#ju}fh6yIKFoV{p+azR+2ItHf$Sc_pA^po0i zjhwK{jt}seJLNDx9Q~85e(ady%}j8}m277n1s{Zs3CvWt=|`e@+W?Rh%ZnIqeSQTf zIfR1C7fvFr)XxaWNB}e&lAHJEe4C0Dr13y<@@tviql{IRe}^IuG-F+lEACm;nH!5( z-um^;@8UHH$Wmat4nfe*qNYBBo`CSeFi6w}0{H`6(bt>+1}Wzy6Q(K7!cMLRLLYus zx($f_ZP`hfLFN?-G~vd$O=uv4)8egO|EkT5ZB1s?>&FQZ<(e>|3=%rh)XT+0#9@}_Q-gQkv<5`< z-dBKEQ>dcSmi1rxfMPt;cCeOCTJ8@8q#j)3X{_0aJT%5}vy*LEEGO0+-2(k2=C|7` zqA;-XSe3i9B87~m^Dx0TUm1psMM>>u^9d;9Gx!**R#D4+9hVSa_u36$9z`|hn)fuy3IR)|h2!h1g2HnI0bx?axBDKEHSvGNvZ7kiL#I-lcUfzsa zQT4iA$BHvEb_gASIW(BQ;6F2c%9=PPn9nr*|_E0lsfNoA=04B)#1X zH+coo2te8LL7{Bj`D=o}QXhmYH@0v&CG1`cKJ(4_O^Rn9Wybuk{4e4g6aax2Z>Gyk zAE>8uXfDexOoH#6`WG#Txr(AqU>k~N+@$8Q6)ziAErM&o1R@VC`&zA0298demYuzh z0?PUCci4)i@o4YL%UUTwvQvY_g0!ub>k*MU5yhUpTWL%GTeE6hM6~F!gFuBCs1kex zepWiAQ1NW=z`Ki)JbD$*W25!XRG!GP$C7L)R?rTgB zjnPFWxZ&Iz_H@@Y@6*_cFR4?tKUz79RbRg6m}9ssMCC$3KB#P{rWYD^ChOL#0GPZy z$G8q8?mdpM)ZR*oGOVRNIb`!RJC*O%nuu~7z}^_zo7NM{mGk5(wz0ZasT$DdDXX|H z%N-;YOE`A|J|^y;eW}f|GU{|e+Eh4C5J{5r#_qqW(ztZv#0UpqUBW+So0G(qEam@P zutG1B(E{Kc5^#A>97gr6F0F9FGb=JfMyA6D?E7%mj8+quaoR zM9Am%PS{Z53Fi3}jH~cWyh9%Kc4Ky)44&$p;UC@^8N)UDAc}?%+ZeZ}9+#41LmB@T zg7)>zdI=?wwV@b(wvy3}=*CLTQIR3XL*)Rp-fCa!$B?Td%&-fpERvwq2Z{D-yxijM zY|vWcKV~OZvxzY0JFMBz(>BMD7#*}J zY-oW|U)^MY|l>c%*UbHt7a*JInG%!=63Q8 zd0Moyk-~jTylK2Rr|uOm^l1~bpEO2Is))&|)fp{LABA^OK*=VLPWel=qKr%fND+dN zHRmAyi)WTPa&*7L@Y$_8j!tbj^E1?Smdv8!{hQ3M2Tv_I;=hUw0LAu<`RoTF8nuKJ zzi|iw?Pz&4nR>gY6Kd4+Kw?bG({?zpl}%?CCOMz6YQ0t$pHjK~s;go(L-vjx>eWd> zHC0wxh~%Jg?^mEt*&pQ^Db6b2BgCe1KP8VE*D81`(5RuyLu#Z0P+VEp2HBow7 zHW(!vd&dFSZ*!B01V@}$Ik7W8k67&slo#$X#Msa-Yr$qhE(I`su#O$d~;)Hp!-Dy?zll+G`d7n-_O7l_Naw zE)xkriTkJ)m9Cd` zI)8qH??$?I>EU} zo{K4IX*~)A^10(N+?Hn6yV;Fn72Ac|`7_QDmh6qVa)lL2-Cx3NY>CRlJN}uQOk?qr zD+Xux!TmFi&QLQux+wM8us%qxTRl-Cdv3>MVsm2@fp!Q=(NoHOnie>r*s|$3xYnA4 z3guQq`yGN4yUbCFddJz7~O;@Q3m}dG4-j)vWas$JQ$8^4l3o>XUhT2h|B|WKW zc~g%{n_D&jx+1BbO9$!hmn$$5ui14_?PgXmdPOQ$afNNnkZ!+jyKYRd&p$~3L-~dO zEB{gZ1`qku>VHAfE-FBYJPl1oz6R%=Vy@3)U!nf+|q$sk?81BPMF(m^5FsH{mYNbVJN zT}mhGnsbW5KlkI&gG_y9hfr7Rr%L6YWVI;&Q{T{9`3njkIJ`pw057>FK36g~{T4{| zLyzThtG5v7S){o4*5kGy%|{?&1b+*my@EPELK;&rhOamaEb(9Lpu&Sn6M_a(>%o@0 zdKMc}pBpmBkapdwOz`gO)sKB--?7-udFv!9&f^ zaHO*nzGU^cP@oPQITS-kI5~`EUT=pdCT$kGJ;&Ii!?% z?%60sM8Nj{eK>XBuHNXL=4$nZ5C<`Ll_smD6>{K*5u%@RllX0L0(34SInA7i!H^lylgik1_; za8lOWQSKJ}Y=$Ox!aVl;ii!_aaVQj)QMeJWLDu;?psGEh zLmTSmq184b2BxgW9E^Ksb*~+?i1~e}w5#Dp;WNHj!C~1zB%Xb|G9y806uvGh3xy|m zcvV~fbSwFRH2{T;z85yZS>RQtsqp&Xno~{B2Vc;AOD;<2{mVQEV>0D|)~E&X*w}1> zgQcao?_{`g>6p&n{Mg8EdNThX6|^@M3a#pho9c_tckv>Y(RYu{ws6la*ju)_s?dEh zU5LSdC;xTM=XT~XtG75*3MadD(P0BfGmdTo^`S&AYRjNlxn*-!;RSki4ADhAL7A3V zNSW(PEttl0FIHMW0m*zyWaY_7TF$f#$hOqUg#IvL{E|}78(UF>iCElLt^>*|P)VjV zwFKI$Kp^N=w$r(R%QTgUY@|WK#Vh_p$Xo82m!El}v?!?INkK(L$_F_jqrte3=dna7 z-vNQ`w2D42wdnCe*%{mrXc+uBtoE`$uT@u2#j$xGMTrqP?CluPez-#I&t``=-9ybc zSfC>)x{_|Z=aC`^dsPk$4;=642!i%?D zdx1FZnG17cB|^axtV(?s6sgX!QLT;@Z9wMk6fJ}{HMd#aj%NNPy;i}&zKZY0dAlZf z#>Fw2`(zPxe+@Xj_$C;82JbK1xrR2stP5ZS+lKH?n3OG&uODz>CeD>w@nG>O!b*99 zGDf@6N?UD2YVunjZ;3+y7CCwSafpoY?Q)!}e_Y-rlawB!r+-vNz~2B-4t zG&t8HtWM{Sc)JSZ1;zzZ@W+}4kU}}!5rHvN-PB} zN+V3yn9c^f=y{df7E=RO_|AMb^I*=UNvharn$Rj_lR@`L7Oyc==5;hS)WA;gW|9Jk zmg8V~WL0iA(4GX-1|K*io!Li9c55!uGo&~D>dkUwLjAo>0mMxdpNXGz58RfVsoB`=XZ~CLi&CBPA z1#U^MKb)>gOtS6qzw+M#599Ni59%&GbCn^0K0jBd(yN8eY*r=p7eI z8OKn^=F@@D4H)U~^#xwR>!0mYZ(~&!m}fxgOmbr@y;=}U9&m?C--R_{mp0@*(muiF zz0o}ObZ!0#B;`F9&Y;?M_N@3^?#kFZ+fk%&I}`bHh#Vg=yDjJ>zEJH@0JKVGVokdtKnJ1`9@o>hX5cN-Onj8)W-L?l2Jz&Df!pxIiRZU;ZyaGIxMQwN2@T?NU?NL^M-m|Bju3 zF%uWf3^!Eg7Q%YYFtEdLvAYxn*7}El#>#Px$vICLKL>;E=jLu!)Pro7q=22d;_av4 zdWy- zY)3^gHfUh{{H_IIy8gg%1?r(Tx^pGUH) zwoKVLapWF2qP#-&roNUq3Nbs{e-YVTs_Bl#Us@-_6vdEHVVj$TMOq>+3+KUa2k6#l zs*BR-TV&ohEQk#oCm1g@jOT=nvq^DF6I*e6p)V86Z!n;Pl3GIID*4~FcOZi4NOT!< zOT~A3q7)@9ZzLj$sHkRcmoVfZIo{h&eKf=)e}}<}hNMt-cK8hZ@iHxy3!_a%D`QM` z|4GO#gB4)}htf$usIO8uMCVn^=ywYL3BE~fRkfLA@JS_P!!w#13?Mx-K?&8;pZvGj zay7&A0b5ff`Rtd%o&k@m3>^d>Xbh={erlIy`{R!G@0|{EIWbaPgw_OD*v=x|^nn$% zs?z=xRK<}D|31#^*z%%o=}McXh!udWB_u9rmLp%6Z4J}*0bp-1l=wG#fUo3Fx`m$} zfu}5QMJ@ePkbty3DYsXO&SlXL{D=-Ht3TX0-<|;(`7#Quf{=*rG% zUv41~)(v!vvn&|9nh#T%Nv#!MHv#AS!;2_K7WSTgg7Z*VULh`R3dNW7Kj#J6N%vxe zQL!Cf27MULU3aKxBqiQB`@ImnP3rmSKojgzVC}Khg)6vkoJ2$Legye-VhEWf{5fHO zsYIREbLo zWKMjwy9CPSNX^C_Lm{R%x-U2%y;>-VmeYYWz*mO7~oOMVf%rL^M=22eq#y zVVu~FcS7>3gDg&_Fn#&{_H~a9L>0|Vf3ufSofTiF=dv+a5C+))4!x3J0G(!mxZW9V zP!;bI^wtyk2mBlbsoPGnS(r zQ$VE8Zv&8~<5C!}J6kNk9Z0f2qKLFj5gPC?u?yB8N$hQwlAn`B3P`7BqHbu#L&SGB z*DKT~#99^~e8oWQrdGf;0ObnVV_nfp=70z@t4X ztW2j0mD(_iMTY~B3eDOjZshWDFi>uSj4kB&lQ&97roOx~a9EW%`N7!3n&cZ-F<+8m z`0sHbv$l0q`n?!s!6KlcvXqvRYjQf1{;&L>!Gf}Hg;#9}NX__h`XN5=JZQGY*Gbqc z0_e5H00f4eDgTa`sF!nwhTHtu0)$PoTVwPv9$AO7?9h1aV^?dKA5@x^<)ALu1QXTu ziO6xQN4j>Z7vn9dc~DjB?4lKIW&fRgg#5KYr<=X*!{Z_U0e}6+u`&=z3Uh$;_r~f^ zHJC)8Nh>^g!)3q1*fW3C8pdSuLlwu+IgSlm9o3%Icr@U`5mNW%EsQuRKS9+KBdvXD z9biKN!L&^QP|meYpknPi#v&7sNSak!r~cVuE*62r*9rW!{!OH(R#FQ{p!-z*|fi6B~lDHxfTt;xkGO`S zUPx{Dc*y6c_OfHinw)KggBL{IL&nht1;#ZS0Z)Q6fcTlj#bBGVKAtT%5r}X6%9tdj zI`+G#?Crf#rJjI=Z*%+Nw>cD2lq3wgm+Eu%0_s`9$t89ywp>@FVsj8ayiqJz>-zrk zB76`;uu6Mm&QzXQDB*6HH^G|_7|u@f`LllW8*O*X`;=N&QqH%1!fw`&%T-^`*kVOo z&wd|4nRmjhL{+d+*7AW|nhB(lQoAmH(* z;^~C}xZ1q=|K)1zLX)LZ;LVE4>ZN(iZS?U0a#C28yQC$aI$euqDfsUziF8-?!4@)? zDO1W<0n+;vQ&f7!*R$`#eRw#;&ii21c^+{gFrVxhdPI)GThAUf@iZV2VaGFtSQ|$ z_;;H2;StfyEEuJP-Jo_euVPY{1XqLyu|tXrg0<`1*VgkLxtAR%4JJTP-AaTW>nJnvKjsE+3lTy8TJk8 zsAwvsxJ)A)T$?-WnMc9&(F>|v2m$rU$2P%1Q&}I)S$dYYAp&%c(+Hi5@NVPTX}*W` z1p9ZMCxhBhtlOR8G0JH7MBnsupGZkiiRyaQG;a{pWC*&u?+au|!I0^m@h*;#cyV;p zym@IuqMZyb8{spiTL5-tCD72xR!7<{x8M9M^A92efI_~~s1i22rvnu)GiA)l8ot4E z%1d~D7n`wt{Hq;QR$C|GK&q@<0oWTF4`-Rf+=TWeZ@Ez`iS>Bpc3s^1B^|BJ_zB!y zSKljd5u@0ui-tQNzMkO6jzf8qeUdkv#82pn?-=^;UsF^uQ!~Pa$y*K+MS+pT+bMmo z>I!c4ZG-`3V6Y#zGzwRS8k35nF`mLIkI#Q&x2qKUfk?4wPn&{Slv;4kG3mZIhU_H{W^%;`M4OP zXw6)rggk?f>iQxiBwN$SjY3*BchWjTCpT5{J+4RnHXH%nVfIdLz*l}Fnt~Kv;Y-x! z^{KL4$qgcG^{*M?$~UkIWMhDCEOHTD;M?ygPg{*eG zXoYZLOjp<(CK;t34oA%umEYzRU7aWK5X~#`T-U9fUD#QomO-c}xId29x_^O0_X$*9 zYS57#S*lJ+Vf?3D+dftK)XBlWqtVt8a!U0W?cesPkG%>P|11Ajn2Zd$6y-96y9`vA z!-u>Ib%S~8mvsRm?+XSGcypFut<27rehf^|_3gj@Sm6xIKa3yc1ZUaa0?0P=1crvi z(b}_I8Q%fs2^dX6nsZkTyC6wkrNB@ z!im(H$DX!zA*j(j;Tf(rVK>+S&s%Q3edcncNmiWA8?}uKxnfy7ZpjO}aP`!brX$7i z&`$kBe1TKu<;f_65>M370+UcOd`_$I&mtV?Va=G%66SQp!-Pf&JzcicVqH*AvY{y7 zA$E3#L+HLEW9$D+8{S5d}aBM8^ci zA_Kp=onQ(msnc>2dO3}2z6yovo1dik&szfbHcvUFd(w;`j2+~ruJQcLPki8qhJ2r# zsF=6%uQYagG!#eGm}IuH?GuRNlQ?=vxji!PRoRcFj`E%+RKGH3dtB+sqX-NWzB@&qHpgv;h3O%=AN9VEc^S}e}gI?37}E64C3b(jNFHj=c>?jwj#z0Ax# z*{U_LQ#J_yLOjZ5aR5WUR3m`1bSEpZLfX>OxE)jpOCl9s4WLlR&efPLsA;Yb&jj7Y zOU%tp>i%K^zRmXodyF=i-4aZqv^R|@oJhR>;_VaDU~TA=xBgb0lDLHhZihfyZI=G7 z1Wp=xr*UUfH3iyU3%Stf%!7_F9=Yh3dwxZ?I)6HS<@4jQ4i#r*s>-FEzPz#Ng6@kZ zl!tf~eNg%X8i=rRgjzH#l}fb@=|S`yp@y z;h9FRb*|&OA4?Oh-B*MEf{@FC^Z&NMqSeDt&(zE(UqWD)oyS_65S`A;30bAb)rigD ze<`&LoPSSd)E%@C6uN<8zd;Mc@$%WDi?b*sU2Q zIW83OT(Jm@Z2~v_Hp26Uf##&Fqsl$Ept!}}3dexo$lnw|eo9FAv46Z9xE>@}^U4_63$J~I(UaBDFb)$n0(W>R$Q|cyL?WPhZsvRkgiJ=W?A|K5 zjsw-&PX?8I8eeOdA#pk-P%hN;0yAXYtvl54vH~!pUdF>h{Wph%aHh1Y2bz7@O81+P zF(A&tAZm=L=VmY4foFN+nwL?E2)hdtA6QsGov{eK%1I)P%!Cc>dAHImRY$27lVN3T z^s4ZvZ0pkzEr-md1ev&qE)llH0_!tPgqmP-j>1+t0UE&^0I{b{$8y3cMu2W#RK-7b z+cx);Kiw$M{dB*d$GEqxSRFzLPj3zbg+vgyPU0@l5GDhZ33o_$W?xi%Ii#<%Tx?6! zFVCg&VZv0e!1^-v?HC!sfx-iyYTH{?B*sMBo}#TL9Zr8%@^9dcv&LFVEwr*us^7YDE(HWkCmlZ~ky992<1j)t=>hwkn~Dxs-5#7_9qy}>^7c4$4lR7kqV z4NyAxq|2$|vWW^zhCGY1ZqqWv8}7dc+NkZCoREbawv^YCbw=pr0E0u4=^EFZr0E#N zW_}cOkgA2RBA0WQkB~uC%;_-dstYMwIDX*p*p5lg=0{J6j`Rt@cjc1TYQxK78;dB* zm(fb9q0*Bc@=4c(vI5&&a75ziufpIvq^9mBpEB)JKafR(glGhMv0VbR5>jjHC~(D6 zIl*`G#vuWh7i}Px>kzbU;j^qhZH)Cyj3vEhnitq&zE7d^Y6?O~>9Vx`E?YWEkNQC6 zX*r9jExxR>b}0V(vIuQM*pleSuP(a_^N<%hCp1Pn3o z23I#Z0;#?6ga!Q(B>w!m+*jvmlNA#gwr`wuV#(Tbp8b9Mthq?nUz*MnypCyP{Nluj zCTBfqd_zk;|7|mSg6VZQ8MdCU*KJXRz%B}It3RkAdp7z+g*bqkkL>9Sz|blAQrQ(D z))n;3NB)!dDi7KK(YI5W3)p=@in_!YT$!WT8zN({0b zBaHS;1w4)Cu;F@*5bY~jYYW{0nl2ZT40vWj@#@y)f!=5K%j*GTv-bxKZz&(FMV(B{@o`Wkl2czw)1k zqQM($hoYS;O|+NHH737t72=FPrq1fAsaB={U(GT7hxU=)sUai=Aq=1luqw=xAY7yT zx3W?N{NCc7X?z+hCuTAPF;0AOH*CXa?6HQ#U|9d2lR@t}W#7RCdhG=MOy5vGbR`gU zLH?~wca76{sMDok{wbOgd7e;4wd`q-g7F$atSVW8k0aJ_-PQVvgrQ%)fo7G@)v+i()UIco6_DKF- zxEWosm(}#ZTi|Rlk(}h^3@*)~TksFB*i74I`x#%IDxAP8%ifK~kFXclNtxA+uuW^G z{E{i{)xKnC^fwIf9a%Pq+K@#1I1Q{C6H~M>cAedzlVStkR8#hQT?CM?7 z8LS@(h#tr~G0W@L!jA8GQ}8aI78vpv_C079>fWRO(e+_-#}ZGSOzjXvbQ183o+ac) zh97E`14B3!{xw<>2Ydp@qB{$iLYPH3O#LSSHrqc02O-VRP9$2|<*O_p=VIVFPvyNI zeY9w`>;R(7Dw;&-C%q||onj$j=hqHmk#coW!!i<1DV}9TV4)&HL!JZU`&IBZIY$hZ zv=5RG(4lUn;LTEW;s*KzE$}qo-f0oKXzF>I5G1UxF2Lj9ULx7dQUa*GR?hOp zMLIyS4+Q4ufb5Lg(QEApc+S6eTobz~_GhvP06k_L147l{xjnaz0w~^A`&_i5y$Tcds&{orIM};8q7; z?@5L33k{=vl>{q5tuOGNoSZ{F4y(!JyaQB`vBz&O6%Dd`ugSuOQ26Or!F@OP9L7pa z+no{)E^+_@CB5>u$vArmKBJwC-Q;ZrPCTR(w&o0vIP*=9hCG)pOQ@JWY2$!JCT<0< zWM0Qm4jL{cZt_awls)_E;_DDLX?g|&E3+0Mzp(78pDbnnXOw_4m(B|~LOI6Bdi{+9pF)Umb=hv&w4tx#Vq!1?W#I9}vF=zh z7sHAVgyOJ-9T%K(_U}0!lt$x3-FA6#4hgnjG=ys}GeRss>9+tt|Lc3;q%lVdds}4n6;T_UyBpNRHUJ1)7UxT9 z>_w6YuQM4+-lIc1mC^K*dSZg{?i3w$Mu2R5vZOIu`RtezCx|8LafK$a;i&BGUfYZx z4=|k3KN5{dFjomy0gS7DA@?bY{GdVg&E+EFD(jnYzY3 z;oU6XS@dt`fJFpVgH1B=LWeJXVKsa*lPvP-K;yvhK6nv(mgL-0lQJEPcw@8IA?)OI zY$m6Nn_Lua3+8&e6(bwwi!0XN!|Lw~&Z0U5qh~di%!M2%kU5#{kHG#sSKjRUQn9sr z;ju&uw?ii&ku1jfYiawhpT{yOe;F~9okaS`b(`{sXqT5t-|>NMgKo)>OhAgle69v& z96zNDf?m)l;{Y7unS@$3Ubu#6;UxRFxuFpj0sCbT7FYTB51H*MQCx-BMu0?5gbGfG zrv*7UzmO^Z)^a4H!EEUEm0AdL;QD@ZRGvaboM1vrW%fz}963O_)T(taEril8?JXF) z`Ml0tN2pD;AQsk|AM}wt-5kRS#kUO!Uc$p!bZ2M_6o$KBFdZu;wVTkRi5oi#ijII< zw8^PNRfbed$Mo2(c;(&xYNKU^qOoi3Y)6xw2^t3`H0qqOa*G#z*!*PyXiq~q=sOYB zH**I5tN)T3idzi3{in2E{QDfIvx+~rtgP@Yp|$;`yajOm|N6s7R`X|Hq>xpByB%?F4KrCso^AV#VSk^lC>!6 z>*^|X?WOU;d-DOSCO4>bGx%%^9B{ll?#a2BhR&$mG7 zvK(5M^#h~UmkvG?b#NkQcYkku!47@*sPPea&U zurT_#jJCI{dw?V(-3ZooC&qgbPJ4~~+GTOi&f!MKCe=Zi6@J313(7_@K1M&sSsphQhoP zNou-d+KzI&48MLvJ7=fdj?1s4c583zqnFrQcU?Ws(}$tI#0;!Jq>~#VQ}N$e`H-UA zahqGhJm93>hwr7pKYT7{;Mza+S?8Ni+Hk*K!Je$0#C{@FzcgT(dp`j9p0FD%noV;4!9Gh>MR~{KlhT=w(~tho1v9zn+DHoi3Vx@Q77V#^ zlZ%&Dw(iDoN^OxAscGm@1LWJXavfg!O2DHllNyIXfhAPDHGrtuLwvPtCp=ce8?yN= z%0H3Rl1`Va`c2}#_*DY#7EghUp=ib;5j4ENlJ~0R;$GRhw1Zq~x-PGrJ>c#aP#heW z&HpUQdH5Yqfr*>fAw1h?E?^D#P`r6#nq@304`ZQ=A(zjp)D7`5*Tac+PKJ)8t!Oh# z!%rPAF7agV0ZIv%GRn^t0+1;RzX9h3I1&Lk>d|rSt|2sbPrToW33o&gKCakMXZLv| zf<}_9&l?y#kfP&+u9O1or ze~Ee3JzZ7Y50yaD&;VlO4w|pl;1I5_RmwA?OyIG1I(yW9A?yGw%Q2X@*vDb0Cp@3| z!VReQp^pKQnq%O}b~YWuMB72e;ShJt9}N8QMsY?kp_*)WiqlXxM3jO?2Kcu17kbos zSthzY?M}21`VtK&9A4&_b5G_^ZppWH&G5!HM!*)Y-0LvJss(K1bCRCH9*?RvRR*-4 zum!Jk_lcySCH1Pd2;x+hLCPyvXAxJ!Tdo`X&tDeERacy&1yL3~;e30I0tN(n2}&fZ zj~xO;-~p!It0G|id`qL_tKZugz_cvX;N`LL%~8j(-aEaPtU?l#h2a|7PX@JOlp#&4 zO@0oGm$j7gp^&0hG&`D#RqPoU>H{hHksnP3;4|3>NHUMaQ%m9Y%tdrmqCrPl%>bpE z)!O^Q%qAtb0c2 zWQyKFAV>@&GD0XoW5|U>vzDnoBv@7`Lh>L*JMz zt#XNCkNpvRlHl10ORGI9Gd(%|4ER#V3hAYJ^SGK3sihaS4wgfL*5-W{Z`O4C9`U2; zENavsjx(UO@aK_pk_T+B z6B$uxEm|+QqLc5%B=-W@JGd;4WY2t>#8vQ}35?1w0wd`@>6CyTIDb3+Mz8z+=zEM1 zO^|@f7NM@-h*90deaDEBKgeP3eWD&><6HK8huvmUe2C&js? z`xmhwNPY-gCM&Z`5c&_kIbnt+Ho^RjR1w1($|=QaRz;~+iNhL~*=zWy&+F@;NnuoS zT|_gCKmNb`Uy`g5TbpbxtMoz+hV~g3;?N?p_ySlU0lz$?pO`m>PsJA*!){n4iWiBy z!F=cN?P~P*(ErPvy{`d?S@^w^a?fT%iLi%5=)N@)ac{~2Cdef!bb050;!W$j)ogwI?{wPuzg5Z?_|OAo^Phydh_7;$@$@PFU4NdVy@JtILe7M=!JC7 zfVOMUl*eH}P495J8Uilqv9}K?hm8$0*!ucdWiIirX+ipq7Hx#wEFY!VF$q|KHRu|JUFFVrae*7y@yhkmQ_|BA?6q ztr3xxu!WiM+`k$V5jKaKL%u_M@m3sT=f6D$g)2e+D^HDc9j*=Wq@=N0`|)vs^0_$6 z_Il!>X8etphguwObKB7h4=JQ22e5x0>(J`fweTuFjPF$oMfEya0*1g#Vhl!m(cL=v zCNID6WEy-bNWt)Kcv@)FP7%OvbgI;f98v2XHz%d;i-IRb3IKBD){VCcrvf*e#e!+m zKcj7mfVF&F6wFkQii0v)K+-x6G4#Oy+(|DrB`sWe%cyn{LVY^rZwj7*DlkIwb%HX@ z<#A+qQN2AGp)AI6T$dTJ&}lVBwA8ERrkT&a2}ehb?$dlU?DFw~4lr`NP#zyCA6L@X1f~=^SgPJ^a*ufEg zv_(fN0iGw4VngLaF0`dSRnibyJ;X~?YD@BKcVi4o;jaT;c1!0wRaUTBLyB=(Hop#q*| z5I+N%5J=YE&pGn;&kl;EP)b73&q=X=3QN}){`dShM)|m&hpdEDZo!1EOow=*)%m>&}zaUz@R3jdxO zPQ=zQ?qm}SI5}=@gd9mR7{U%wu5#i`pKOqN626_#aX0Brv$bAf3^){aZT_4HCU&Lag)kuRu2*O+{JS-(=aK&{o*n?5VK3i0PlLu zs&wU{+c(Ug!r!oS;M+gpHQoYbnDyCKbL-vGJ!(nLNS`how;e`el`k=DRl;b@6(Jx`72O?cVsTzDfXv=nEnnr-i- zF<|(-{P)-)(aOX1FFZ2~ibsj!--U}8p979i&XA!M>p}f>uoydFwfqW6rgran+TC-} zBVOFy!1ANDYv~=2*Mf7Jec(tSnVNdeoCu@ix@r&;6h@zr9So1ll8pfn;~Clo@?kcM z&!HX*uOP?;E!spq0GSpN?4KR{K=`HL8JI46vq3*4HdZlLl=Q$9%b`kJ)r!WQuO_DQ zKy#tw(rPYPaOUWmlhak2ENXLMGUvoEob2~|CH}$kgzOR>vy9IBX3ecwEkR^@P}(Hz zD2CEMJk{NKb?YUzuikbUtST)L0lhMJ#!)bNS?^5#+tcDC))*`9&Dn$w%T+;_$H}Ed zLnnhXVDeAS;HDPCPQ)ppspYcVh4C;TuFSao(KS{PwF}{cz@lxA?NQ-qI@O{A5iJd%D508y24rjc0?n zVVw0alj|QTu3`JzJlNkT@rN0vG80+*S==>o;4yz{ch}wX*@u?z7zM!8GFGFi5^V+kWfco`aos z=m1JNq(i@h7q&5tId#uZlf_X##0C{He}z-t`;9P`W*kz*RO!z70JRww%J0@57Y{}G zaNCR(96l^QZH9R2)6>Q z(VhwL^5gaPS|D=135wr1Q87Nn-lMU>X+oLIgJFk)7L+StTQVakqI(I`3Pu;QV>;+P z48}iC+QcD&HiGlri^qg1`saXpG0-o{IQq#jb$B;Gb6*El#f!v#DNSm-31Bf?Tu#nC zcZod=oX#!}%jy71kCu-ON2PfFl20PpCAQPWznLe*jhiCNqvtUZAzyGCXrHzN~Tr^D4QhgPDZ;RpwlYr*hek)mg?t?2~!7S|_C^X$mM z`b0~v&0q(l?f&KjxDZyCfbEzP&U@;L{g#Omc|IE4r%|x)saX3^0FhIz0QTC#gbgtnDDPxcZnPgWrRZjSx}mi0XFX2L8E!x!&V(9jAw>sTK`& zjNB19^mO_{)s<$wl{N?Op=M6vYO4HgV7YZyeookA_Ze+c$%)5lhXic3#pdk#v||5k zG0b;neNN_1Ma>!1W-45#4!apnUdFX(Nlb=eNm2eaOR@(0iW^$Jd`7q z0>yzhq`3aV%yPlsUppXfWzp;zaT%c@FKfQY%2vhSM*X8t;bQwP_vAP`4r&EqQ1L?j zd>DNQn@FY%HU!oJ1XKAPiGLLAj7Gd>#*oAUr}8MMguH0YW_^~wmO8oM?fy+cSU|fX z*2&IgvKv^P$?3l8d;FVZ_u0=NHip4qu0mShf%#uC&`0ZuIhj$um9+4GWaFiZmkikO zyHx<7qIwwtmroEZ{(XUs*XtW8)piM*vFGtCMzATjM?JkNxit~i>|Wk~yrfGs%o__) zM$y+5m#qB^k1;o73ZKA^f?1 z3lS3_MbnThM1Kc+sVliGt#7m3#qp{4{^MeDp~rv)VM6srjs)qSe@$Z=%Chq)&L@11 zGlaj`XbLmYw1EIv)R9V|Jpw3wlRB0EOJ1mNk_(EN#nRG#>e!Lw%2 zz3pveokYc6X=sE0b#vjhY!NUvJp^4is%(=?TZM&qV$56mkyAuQz+RDq9U%|d)+@Lu z4`gelhKxc5E&Q;@< z5Zy)RK;${0L;Y@xu@NsufL@>r784*N24LtUE;M!MKGfGevO#asWrluL1@cM;Y!;Gl z)fK_lV<>nsX!xOX(OZ!8=TkpE#vX4M_7zV!(B4i#A0@ zvDVg?Sp-Zt!7_*v?>&dW5h8KDz&OE6kBwd6Wgorr`X=J_*%a;4O5HFJYfyVtyT+8x z9DY2kttXPSs;KO0uq^|tfMWlr&(6nem8IZ-qWgYZLx_sVh1)bh=ONKRMg1vDljDZr z(%OLIx$919M{DJ~{>OzI>~$~~rlPe9;Kv{Fx%dC_U&G2wDdcvsVQ9GW48U$$8p0%j z3`bC%2f#!XSrvR}zIZ!DNdYIi0B_khI;;q~*kuYY6Rr|^y>rOuoCZY>l9V$)ozDZe zqdZck9t!yz@9#VIw+Nw*Ql1tWHi#vqberjRCret~ZB(u%FcaMb>$5SdfP88BRy79= zP;JCRF4QuAw^_sdsT}LW{g!2-kj*g(tI|hK0B)ZuV6(hEGE#XrK43kWL}8(|Ld(O0 z9bb8!J7aXxrW{F?-jh}Tk#rlo5RyDZEco}$ab{nf#fEOk8%%kJI%#oqIObR|XeKH#SEd(p<_NYr@t{m<_r=*VGl5n4c-v z*gnXta6s{CmKZkaEo$sdwu#=<6oaiUJ~;eSJ}b93OIXv%JIjNqES?$SBS}|s9>_Il zW5~+ftw$@#OJVj-nq>mtMY~yiKU&c3`im@W_ULfnGkKm<&SH6U7Lxqa%4&S%P4<1u%2WtEa$$vAgmp%BGZ>{ zp>H_g`x1NT!S3{i@bMBak}Y?bH>R=JXg($4f0F0Ze%@1*1@ZT-}RYn(W~{TM6n(MLIDx-RK~#t%mI>+O~6R7Ws|C9Jtu2w7W?37!!1W9z{l*#t*foec{@GqlC|+Vb4WYPx{^ z0dz5vs}f2YWL9b9>9_gDbHLz&l02z1U5yJ5bMc08LeDwMa_VT=?QN!qn6%hA-SR3P z6pzq74zk;f<&~QsA$sNPIsV)XIU0Xtlr`W6lC}JJe#qWhkD(mO^AWnnUzs~X2oph8 z{3OgV=$5Ns)ckNK+_$L&m!@;8_vg*1%y)If5(P{QJU_0ji?hS^O8ri)fM-iImuFGVnwyhkY%j~}GU zSkP&0jA`^%Vs_FDBGO5owTdb)MS*d%D=fIs2T zfKE1b44n~1&%k&{9>oXZBx88J8zCdK_#RD1VGQ#&b$+%KJ@%1~1CI<7%DZ0G9^o`$ zA{?pL0t)|fLfN}a_IcsoP7GFN;6CZ&R`qI;*0_ z)imbALok+>XNSZ=`1qq_-G-?ygR>1w)K`m97aIzoEP_B0 zE_cSv;8`ktZW*tPTjxs*n?p5f@?qndn8tWmqH}cE^wM@HM-l1U5}&P^IoK&fSQ_F~ z0YhfgnZa&3PS((8jaXMS1A3h&F$@rdaI3Z2`vN$F0)<1p^cOY>T2lRLfI%2Tqp($z zuow#mgl8Tpsw?&?199=?h`7yakL!lV`zHuoC-qwosmZzK=A3sU1R7q&pk5{Fy8v~R z8A)@Vk5viglq1}$*9Kc@i@>~;*ZAGIAcx zP8Zg$Eu-;43WKG&X*_$1V(1vLEj3r9(8T!)>kM=fSx!7}ZV;bH@fIaG&ExB^`ox}=9~VPG%whG6p1A!dl^{h!+rafl<^EZ`gSz;_)U`>JoJpeM!#SLGd>2|2Ma$X7m z&CsGHpX&@fjMiWc(tw;#9j8iK_rIfm^4=W=X;&cW+>}q3!?jpUj8(&@D43 z<()T6H#3GxdC_Xh;mUD1^)otsBo_hL0ajM*^q%!} zbD#bhOB-V{qulScI)bDH3Ejo(_HUTeWqBG>>28ShFhL#0uQlh#APq;TW4k;Nq#_o)h}eB&DpNv$jHkDf0ezMY_YboQl- zTN<-6dqKp@#XyR!o!%d`9WM{A&Y7Iaog&4WVO*b*EaVoJ!PSlZ`uf@Ur(4Djgt%UH zSh263hnY{eq_dy-);X#mF6~6!@t>tQCL3|hVdO#4!ddUbXyrhxw|HKgs$ugBK>7(Z zQ{6)rwT4a!%O!8&!SNML8-S&+CAZ7QL-vv9f(!PilsA2qhsNms0sb3=-NR*{8XL<< z`Lli!{TmeaTQK)}{f$OGHl`W!0a=6IuZhsG`th+;{VKqC`nie06)H;?Ql>uX5sQM7 z6%Q7Gj%}VPy3?_EpUh>D8|y~gtNBstc)AJghNWcj7k@=wH*Pl7;oX0w)ON5zAMo!_ zQV^wvZc8hE(@rXwyK*ASf|6-l94$pZ=m146b!y3P*}OT8R*CxDg=GfGJ19Ad1CED6 zbeCA_Lw{xjZ;h)Nr{zN@THQvU*$b11$FMO^&A2UAZE&>I93#){4T6EBCCa{!%*}O+ zo=XNyqxJ~9!WTop%5zR!_IP0~m<`=7xMYV*(JG5VG+ARPJ7)MQAO}ihs(ivQvwk~V zn-SC?fOgV=-=mq*o{a14_hfc*a~3jsAvy*XMFgH)O;>RKjps|Q&dk@~mc3y_e|!5T z)WoQbM{1Uk$Ay5_>b-=v(8XMJCfiLJ825_N`!h0#KkT?6pvibGQe;ctz^;|pp>|ye zT#0x3B$7#k_`ZnTjJb7Bt&u?iu51BhnO>0e@*RM_Oe(`tl+H4llVD)GAOjrIxx_U% z*xIbDU^)&zWbnv>Kx}lf62T zPULlLL7Ej%k*diqmqX^n2c`7VPpF)J_5K`0dlfvb*wZ+iI0iJe^UPCqB0JNPQ`kC2 zjOzSUZasU`gbC%9A3Z?~cn|;w%kZbQKOX}#B@}qkgnW82okL9#)we;qr6n;Oay)9d zx*C0UI1g6byfa=5^N^!4zqyE@pRfRp3{ORRmgE0rdmUxcxGfP0N>b&wKTJ5(?k6PU zdZD(ao*H&qdk@z33KBK?Fmdv~1bBrk9PB1{F46JO9BMPN^8fczZxJY}sblO{M?6yN zKRNZV^b1@C|NH78ggL>W0~aIURc8zAL$rcNM8pm|Sqq_hMqqTMygS;v<+BtLSkCl+ zZsuAU#JPMeo_paUw%VSOG8#I+U#P^`pM3W0^1OhC{keLjS5niG8JAP^wR!LymdY=J zTL(c&GI4ma1WG*R6FCSh5Gh3eKDDBRYxn(3U(a#Gmtde4r&fxw|H+^-h6@>b7)4v0 zi8S)bpskNUc%46^mD!r1ruy^X`Sz0sAIMpXS0?=8!ml@^UN ztl@CU{w0BcrO%;yJ*#Ro8$+Z>pcRcRpOdy?l)eW_zX}||OZet2bL!1*;xq6mrv>uU zZz>r2%4W>4wD8b7OKK0crlM{8p<_&|Z*lrY_c(Z|7#C#AGEECoUuGZZ4B)Ad%Kq{` zid?>&GjU7DS|it*0~;aU%zWoXCzchP=_CC3Z?7ePfH(;hacjBBfeBquh=Y*a?cJgs zpoCyD$apeaJjEydX2i-mrGKhZf)qwXJTiCwnyq6NqQb^ila#&3$Uh70J!8*$Ar7z4^19;xLYmx+pim zJqygwkYvGm^PX`I$~*2r9EeRu)_A8S2f=*+o=vcT&%C$@W!x${93hSY5)z%S0=4H6 zy4K(2+l*1an)VmP#gEl;*i)^}xwQ^BuAPRYmO^8S-DXQ~R97Qj!BM&Kk)!bu{hs%H zroOG*zXn%STEXGdqAsU6)s{hBW+qn5RCuUTL z&a{J1$igDrprt2|#&<$SPgYdnY??4unDL^B)JbX#*`8H&Ft!B}#(Aie+A;c`(` zyX^nvzn48)!wY(F-I>eH-23tJ=eKnDK@D2#cky%J7^cBLFCYP$kIo$uG~lQ)^=^wr z7v89a8PhCqjrKIO+`Fd{RmH4+d_HpA_qX<4?}*0fvwJ@O@k^J@x+SK&V@bLUSNYk_ zoAd?w*5c3^avCrD?`4_hIu9o5BMsS-F{wi~KY3YK_?5jfQa~Bi`{2)@#-uKu0ec&z zO($U99u5yrUcTa%n7ol z)xYpzHB8FpaYNW7>{?Qig0>E=fG`7q*e#?{s_%i<9;=WNn{^JtnhnLPdf^or7ExD| z>5(V#6T;V*;)g^b1Fx-)z&$ceU$LHmHF|}mamny-cv=x1phNmeo3%HRek@Wx%PLT!NZlp9a^>{ zdL&yWm}lK(U*?*B(sMg)nA43L24}k+xaJiJvT`L)$3glk*F{`K`9%4j6Rv2RYX(j> z_|@^a>wO(nsK+?WreFR03l}?KjhAC-Z`fC(wdGTi+eqICJhnjO+o=6AS9|S*Z=p(g zU;9Um&s@sjk%VmWe|dHLaTWUcSy zgaMML`}y?43H}rP4MNY^<&adyEokwy`vvdl@s7MvaMPcz?3;m5lv%|TJ;d7TcH{i7 z??}TE<0P(|@pg8Hf98g3Nd)woS&&!ppwnPi&e(XxFS;0hm~+{s^Tr*$95W^f)~~st zkKmH-lnFeyMq5{8d{w{Bl8k7cQ;L~1fsf<5rdTZaqyocX2`Eg)7+Uk_$`w@%fW`Ac zZz^g2e(+Bkl9A{lCVr`xY#7z8Or!LFt@Q<6XjmSDO}X?j9zhKj40%~$DWZaZ2ly?a zS2^=~o%LwqGIFT$+Kuw#;ZI_xAH88p@$Nvh1^+G@!}=nhn&fX(0x1o|n?f23E4WBR zN*HcstbQ=mT_)B?2QDJ4(-FW}MT6z}9f??D;e75;;O(f_teH%6Mc9Sq59R?+Xo!&6 zaPWc3On|5hOEyF~G?RFb*iQ^QU`wh&`WFSM66a;{n43wnz<$Fu969FsxT|@DYun*k zCYHJ#3e_A1WtvcX9Eo+wF}*4TAJ$jJ@J_j`y*^QGJmbL{sNKRt4uS6}Xx<-4h+fNU zx$y%%G$oinaxxDxR}Y>4ZTR0d}o&ZNT80|D#}MDYna-$#Gg zSmJA8W31sr2+j*|3{czzsK;K(ZD@Ch8vz;;QyTs9fx9(`q4sa&L1No@YhSkw@4^) zC5fBduq4dHs&}d~PIZ+PTNSnr1}dRJURIFx>C9XaBNW7xGFtZqpsm`%M$=P$#AzZZ zV!&5cB~FJ;lf^rRvnW3jk7B_6I7yeTnyQ4F*Cr_{TRaPSVa1dO0F#IE->vSjiqF>6jDXNia7c39B*)lWuLOD1|&macQ1Z8aSJ#38tBXzND>R$~5# zQ3JYftt7;hg_W{Xjiq5z?Mf?}g#6ZdOe_nX+E*^Xu$E_zv~)Lfg`$5QD8&mcOi1$H zR?8hB;$@zrc01j8;{)iM+dK0g0D5_Y9G{--e1^Q(EMp<&;lUGDk9W1q*gXkJr z$cbZOCH&BfeSs)P>uZqs8`%Jk@}#`H=85K5Hi?IQLVlzE zwbEO_m{`D+mND|tQUEDYt?^l^ivg*k?CZw=2juQaEXk562(9*CVR2x%_48bGRW%3E z3MU;H5562A1>ETtph3tEIzx_;Yco~TENFiAm! z(%t1l=dE z2H9GJiVct6J0ET#Zhe-Es(Jvq?3{tG3g8$t^=kYzbGZJ&MWTwS;n>}$VI_gn7h_MzXQEr_shBJRf_|fqF++%zslv$&)=X9&2=MR3W}`d1 zLVm1460ox^QCJJ?vcCA4c`b~5@-~{m%IkP@b8Xq2T!uk|-KecE=6NhyDm?GIZ3c^9 zc2nYsuuI#yGO6Ky(dKl4iadm=0W_zL-Gy{W!V*aQ;T-3u_OjCABCDHIj*(X&gfpJ~ z=ZQc)yp^I%r0W3i4|9yL659_c;uZAMV(!^UUR|$$e_tM~3trCow$Ki5ukFr93VVr& zF&UcqsHPbdHcLO=viqkoU=m&3)K^~G{P&?`qwx)1%vH;T9H3G5&U z(mp+@fnW$uR6(hj+u)Q6swRY0i~CjJj&CNI#JIbO)_m|Avl+{Dg|Gn+TavY3710^URHm@DSw~tPtBlX*y=5JA%WZgKfoB zMjKl{=f0n|*a=Cp7nu=DQa*6h1q^KS%k)*a6XW@(k^ADQjEl_XlCvR!eNEJY%gNW0 zFRMrXO&kM_Y?AvxCDqxiZN}Bx)~57V0|Gh20j7~unK_Gf22`l=a0dI5{RbsE5Z76o zLbSfT#5sS5tYuR9V@=LqDi#-;F1~$|RGh`PnqOJf2{wzt!cNdT-4Dln#A~vMgDaMt z@jumrqi5cnNDss0@jr9)Saa=eo;N>I+avi(f%YJZrBl_PBbVX9Y^cnjUp_Bp=g$sd zSjN=8fa1DZYMoQvQ%^uS8p>&q!o17u<6w|jKa4>t!h$jI0rGn*oTGUKc*D6<`-&p zp@V>@u7c{^^COJtYDIqPb)p`Dx@V1&p)`QV?!>%hV9KeAB(*{Rg;#R{c3%q*p;irj zQ_G?ya4z0-eG~=%bH0qQ5wM`oNYAsy!Xyp9rODryr{2N}1U*0QMq*3ajbjS|Kb(8U z4k4dmXP?mRDv-5!ZaB`;!cB@$?`)@ULbr#5tPf)#kkx@^>NmNSEw`Tc!Z2iz>>eR` z<$`yNds9{<+Qxr6hpU0YFA<+eeBZ^r$iDcsOKU5{<+22KI;&#tw|O!uUmWRs0{~Q( zH;7A(JO}-!_@(WDobNGVZ?(hzuonM8@2-nue(a>mO28g9gKZJ)N2YpAVf8-zXC9W* z_t$k?Q$9DU_^%rHZSjptH)ygAoM*8i!?^d>UC25L*s87NvRZFtR2Grszm`IZn^aNp z_^F*U`D(C1P~ku{{udI9^x+ABx5P{IkYiPr%0Nm!#6+q+?nW?)waICbUsdp$HpB+x zF+C8BRvCM!tME1`~CQ!#&B%rFkdLCn`Q-r2{N0uoT=r z*E|5^muil2#wifh_H~P6NNoJ)aNMx-J3rBbLXBKGg|X) zKxp!@jm|Jn?SD%7UUEqVPS~Kl8*HFDl4)ygp$I`UCw*Pl3pht)6RmLhc@bnb7&6M4 z$E;qC6%*+giR%2M6{ICweH zo9OV@^P%@o6{<1c8|J6T^P}l$-k;1w!Zmy|+$fxrn3XZ^h!~Jf7oK=TQv~ejdic9Y z4#)8hxU}>))`4Y^y(5}@%}0u)+NBQ#lE~^o%8qO#3E#huB=@;T8%9{Kmz5sI0|g(K~Z=W$q=yS-u3yybz~ z7vF#a^AUm!Ljet~dBfx$I1zv~Dxoq;ccq8sJgF$o#IpF3Ozz+9K{Y0AY3$C!@T{L! z4`q}z<60nP>mfc8FJT0kf1OC-u0W^`2e?Hifp7!Qu%VNtMlfa8?Tze^Hd8xf1hS#k zbXZVzo0zD2j*VmmBVCz^wU2P#SKuA{&8(5vn|$_?uea3@wzR*eOp%P_y`*yCD9>;D zLlu&aOcR!-bRKG?{gfPE7PDioNC~S{8Qi@W=6n%TvvwS=TzVi}u7hP)=}&4*+n_3V zNC>J3p${wRY1NW1&it-cP4}K(BC#JSIJmMq1KgvZ50VQ7|E!Bw;_Y~pu|z+^7)ooD zgTk#K9js`hHH#gZ6Mrqv)>3^f5p4P&d*2)#mrmnhr~kTjxI<$Xr-k5(r)~i^qVadb zmWusqhk2*-%TvW*;#i-h28NW;IS@uHU`bLa0ZX#C5b(==yFtVY#|;NRF9m}SKZs7V zKsMeC@etgJ<j0GIl!^G^&4LNk5;yPXK! znIf$yI$kF>5aakt=AAr=ICt*V;OO5ri7Dy#tgCL6*H;HF3Yqj$ReaZ>R8q5x{77PF z@Crn7b6rX9mBof7$H_4Lvi4*jV~p0h%$UtC4`-T_!tdx%4=LjCM;An!#Ox$e-ACzj~ z{S}dKRMXqh2D)=s`a<*tlDGgHj)0^3X8vK|AI z;$z7TbTcoWV#FbW;o{JsTKvGYudDgvU4LY9T8U$)0oeh0D{XOL@Ed6-{>qZv@f^m@DIy>)wLt26OdBDVXB3=>=SXCF zT*H0)PTf}bjGA+H1*4ZIFp+*Iw7*Z_wBrA{>@TJcyZgDjt`IPX>)qW#)70y9pnsho z4X-4LIDOQgy>LUiq*V$0LI0Akg`J!>z1@}`uwa12*e~m+L!MD1e~u{* zx^29!2dYL-eh$wC6#XnqZ^ixblT=#R0Y&9Q17}l1^o_QIPn%{7q5E7;Z1r}1zEA2n z(Gf0ug%yQvoDUNIL>m($fxp{Nbg441;~3dXNAoy<-MO67hJ8VWxXB~|^oU=2CZm_- z#gr{-6k_=J275e1Vev`eJ0UOm6IN4f={0mdh`8<~HbiFP-7m2om87xgK&OHu+ z1c`4>K$&3=+M}Wo#!Lm;C`v#K4!6ke3PL1C@iijdA!^$id6jqPeubK-b}(TeL8PuX zHqoWq=5Z1bHDgSm=$oIDxy|;Umr_$WM`O_?#JujshyXM(@0{-h7E?kAw~aEbQfRd! zb`CZHzztN{;U*jzL9N6_bK; zZbtQo13zVE=?-LB-Qt%THHs@o+q-QUxW0@)f>x(9gNaae?*7$XL4UPPL zn-#Oa|LH-_OIE)cIu{s;8)^g)cJ*8`-+JV1WddGXYz7yPnH`GyfBBzK`yJ>ZpGaE$ zMgyxBZ#;i03p&F;0Ntrz+<;MzNRj-D*qhDJ3_$N93DRO8h2Unkq*!s#i5)PQ5ZT|C zxd1CRyQUGfJBG5vViwoR+?GQzjQt_5z%Y2~UPyLyaEl~wuLWi_MZ@eZ(PuvES2B!q zD-Ivti06V5t$D6tcb3UJ3FYlxwsyUK@ah5UeOyK@`RbN7fJVxtKadsiA&p1f^&opd z(=vABk9Dz=DKK*x#bt4}Q%zaSJ-pGbFYYQ?j zK~Y&0&bgS>BJR`^>K=F?Iwtq~#u~KEgI~qAIiWzA>5=&llZa=!WNi@8E1@`#WEe74 zMW5E+yA<{nqo3qX#IabgH#`dV`bSI4ykO1=u~#eN8~4d^4^<;KP@P^G%Dsw#l0_f* z`rV_ImsEL&FX13AXNU%X7qe4!d^$y6%Yx~J90h@oD6+>Y91M<$v)o- zeBj>-`hx`g#npLboyC_gSw@LEd4cBeWIPDSLfc{rjpUg1fb>t)27yy1;FQ(BW-kZW zt?QGHZ<}j^v$cQGYl<2v@NByD@d$32J*>c}#o+&7rf*{U7mz#kv0g%+EXBpNLTTrfHIV?lxM?^D1PFCeClOf7yW ztlfQ=_M+KJV`g59^;P4%V{*6Jkauz_)Qjr7=iZSS%6Kg3P65G0s}{Z!oHIRW+Tgam zKf{Qpe)89yN>h#3=woC@287769ofEB{&+e1ETDAZ;Hc6L^!JlS@b1gZ!rjx1IQyFv zQ{Bb9o6|b+`a%s7N~xt3u>(Mo-{$-c_f$LU(aoa@*=S;miG$@ByX$>f02tnGC_8Iq zyQkLdye#*YY!AGX=V7O|)|`6))RUH&QRyEr!Ws1$LFJI|@E)-81FXa%dF$MTatfWI z06J#m&}jF0HO@UzwY5%4PmUMsRUDX9rniV>jI}f05(6+p(w|O+GWy)eEEsV{i9h}oB#_8*GdOvSSNKK2Omd2nQ5fIDtg zGV6N1T|v=>XS!>JFH+LVGKNnvuZNHE?ZEvdP-3e>7j~0Y1)j0^sVDK$+i#v*pmTOx zu!F3lVm_qFt*PxOcbJQu2>ogXo!|ZbiN1Rpqx9~5R`3<={JJk(bhUyXPYn2YNi`^N zAI4PT#k67TpKsZc{?gL?OP{%Ud4`=kJ?yV7Y8qu*#_UvF@pH@k?y6?)%K#!eKq9yU z&Xih1o33o|hExj?k~KAyU|4L;F}s#mf|08%0o7Vc74x_)jx4!3pPoi%cCtf*iE&)1 zvz;#IA`K!#OVX}{p=p7NZ^U|wCJ9~D7tugT5p3Pv48gP* zC0UEQ`;A0IlwDIU0|(PuXChCvYw6{)ds!+axLab@S@&jQr#^1i8gMxdHV47)S5-8) zmj9z<#pI%}c_#LQYl9@?eCx2PwTkXUdNUr@bj0fe%F@DvS0f;21B_czp-@y&!2?~Z zZoCZMlmhTcx_^oi8@CRTr`+(7kknK5_6^?aK_9SvoX}&PTSQx9 zJ-o`(-JjwVq|Ns&!0Sh^e^n{HMioO`x7*i|8R_@u#Z1e0qf3tS#Q z*QVy_^&^?}<6prH+#`PzpsTR*`?eAZ+6XN(qFG40vRR(CKv>RHsS>h=7Fj^;NBCu` zru#JQ%S>*mXqZtGdHbRT*Rn{G?=kI-yQ!+9E=+?c;2?{CON4pRvfOJQ$X;4gQr_A_ zYF+MFmVGOT0u{EKC|lA$31yHE)+9LUT(Fxnj6$dJp4{j^wP(9g+wr$(CZQHhO+qP}nwl!@Vw|}@- zL?NNF2inG9ZUr~eIfZlM2sx3-=!g>0^ub)0fbXAH#Tx-^q;giZKq${#`xJV{Ogt^* zfm)$-I`{o&C0b+iN{yJRESnVO5L{Rl0IT9(FY6+jLnO1{ILcf4{Z~qxC#LEDhn#x! zm;a_x#H_XDN}#K*w_L-V?}i1y`d>_Uig$ae%(1$)0qy_G|M4iyX-SDl98)nec;3p8 z3J)-X>^qZ6+B236fN*S99_f~mqx`$3dKgbKW^o&M4Jk4pXhKklj-!-9p`(wo=!evS zHkPu70a|cU0T*PU8J?^Ib@C23heJ2|Q#KDIi!_b9vE^r6E{vcce z;c})RPx?_P{~)(q#xFp{rd#?9C`v}k29ah%pw7ub#ZSlOe(e|lIOZu2Ae>#ZjYj3F z(=s=Ev-u)q1JCyPQ#K5x;#w>&-0NgVH7w*7H|X=%Q!NIb`rRu$V9ws~RK|;L$M-!* zb<4IUxZq4sbxH^AaO}ANS+p7x;0oWU;ObzUK4M&9dO2NU4m0w=^9&|CN%LMKoC$4o zei$>4VAS7rZAW-bQ%7XdTq~`t0hIB*H-=`Imyfj z3s+Z+yM=qAixR{FzITvttwk@B9{8X!1y*gw?rZDwcHV2=uJgZrCv+&7yfdKty-HAT4QA6@{4#|H1nlp@_rcS zDgb#H2Fdye;3mayFM=E~FaQHPeYbrqHeclr3uWPyyqs3qBDx?lgyP)LY%|4V(a|5H zjjaAg@?jWFGXcBJkdn7nOX$7vcOKT+49P2%8!{=p zy=MKu>I})M_%?IgfhIa7LoY*_M+H1_!Kf38s=fss50lN7LlSbZhOInpKVknkuL2xhl^KYk(oKaSJ*Sk<8PtWHe}j!^QSm@D4m_^n!Wi zM~Ux1bf04b!CM4*%_k~9RaKQcBkkqa=0bD?+b@?4B$m72K$o(&_4`Xh&1Mdix2Fz2 ztM#Yy6SGALv1&fOl9x)%@Np_(@)(h?iJ!)0FcZ)UqqtB=Ya#MlA!BO=`k=xT&a+|H z*ky$K#~>P%ZD>iXuL2xsgT;;->~9hBwh#1j2HEwnJ?tf7whg}~c4 zh5W`IgRleNWGv7pNOe24LG%EWdo2lVvS8UZSt!+Ef4A|>l#S*DYpc!$5|KnKr^$oQ zNO&18L1VeuhnkomfJqoz?Z{lK5 z@g!d3OJpY3%=0Q7#{*#RKoLa13A>j2CeOtg_iw9G_%bGyG}^ybF9;)7`T(7$ppK-q z<;9z`cx8N=u;=-`?de5Ccv0(9WHLGxM-364Q897DOIg+imUJ;AowHHp9Pu+nM-Hd> zy44aPk3(T9Uj_kMtIMX$V8WI~GHd;%p^br<2^;y;aFZBB+!SUZ@$9fLnxBd5XN9Pn z%zR2E?XitX28tggm{S04=2)|+m?}|~)pZ`qE-gi1!vC3w_pWWzOK2)`C8S3mAl3fU zcW@j+6W3X4q8d)hf>-)B{>B_Br0Na=?wXinwd#a5t?CT}kB(ru5ouqTFQVU@mI>G~ zoRiS`)yXTu8cjRU3#=m;2#P9`bPcMN@K8k-H;7ze_Eg6kHK&Ws1o@Uq*@OZzfgjuY zEI-Vr^rHE9r}fM~F0k$CZnt6?9lTEU4i4?R^=KzF9Yz}dk!N%*HS=WEr27r!Q$F1* zx4G3+eD=>we+)oPb28?CoC8eqsA|G8|BYmNT;EeyYqA4 zck#MU?Svy~Sq(w`pZjFD1{6Lap?XJzNwOS$Hm`w(+oSX3F&m1dT z$->fpvZ{Qw*v0CcJ_PN|eEU-(dRvn}V`y;oTHe4s52ScW=BS_9>i2@&)l{rj;Q3lI z(vC*2N0Ts`HC)(ztqlD2l{b3t(_5nexcPxkc5^yui(?%8vs{z#TK!4Fu@itv9jP$c zq5D%!{L0ceHg*QxFR!Mn_QivHDfpJ`Zr$CDsN~8rPBqTyaw?J2eTHGP{Uhquzd?o>B^H6Z=L+4lOe5e%(7k+Q2*6$=`?V(VULZZ$_)9g)#3lk{~wFE z+&; zKhdYq^QS#K{#^vG_voY8YoWyOUfSKl>YbzR?G;5C?Knw1y8PEx>+w7CZ~34p+>k10 zMeTzuV{{(fq%98(g2WP%Y%26UZyYT5^(G$KcP9!T7=Y}p2kuOa2LVo;(TK4lNCcA5 zM`G&J*8a}y%qV>hicSbW7Js=?jA=m7N8<- zoxyeYxOIgG^lDT%W|gneG}{;rwYOKYb~wRPs7)~LCljyE&!HD}9tp3#8Vg&#mJ&`^ z1zh%ASn+T#dV|Xku(j{dCV~sJV%jI^`u%%9`PTfq98vrLjf4~~=#AZ(LW=UfbJMt4K|35JWD%w}nBDq6@~ zxfOSw?&v@97FPNOFW@MoXVf&}GdkERZdgW*x_Y-(G9E1cUZ~fxENTnF&tB_Q4qMXk zW=9_hvRn4ustTFkgG-m7vFyq2pvS~YAQm`{fC{Y2qN@W1^S&OgS^c3`7Gy?$+w&UP^#%w+46vJ%~X zIlOcEB4Gonln-jj=07~lK}yg)`UFW5vwYfNWb>Nxq;Z-I_V$ga4f@CX|}fTP}mb!K*ow&I2|Hd;v551^G1g z6FMaR_+3f4ZGH@R4$4>aM>{mxvRH?-IKBrLHv$+8%%1J|I_7dnIz+toIx7`dv&`{E zz{3YBVb8HW+P!$yt5tqib!B{7&cG$bo*%XPq zA#XE_P@c^ste&I&3_DOu9`cT|&smI-lw6(M}Ca9hyTMJQF9iTA^rVZ#EUw2iuY znNm@jrBBz~r39&!myPJs%4$3ZApIpf%Xt+m2LK!K!{%_UJk#h_;`yxIEalpSBE%Ce=d zJJ!eOcfJL8OXja%pR{1*i}?k=I#@!&DvTaXzVdo& ziZUM$B~0|NblNQoT7|GuGe6JCxalM9Qfe_1-Cwlq+Qbo>kNT~cfTMMI#Xe5Va%m(ei=#BmTfbK|~l zX;vx8{-&sDj0Tzb=08BO!2j{+b*{t+r=a4O^?dE`Re_Y9wc!a!9$Gw;Glb@BRqs*paZ!H zv!$9ye@t*m6ZBh51*rMdE6cgKjvYvWlYzccZC=06ZKP_sEZTT|cAlLK|5s6AtHN8y z^)Cz8QXQWei!S%~De;1p7p#UKl3|6s7+Do5yYwdhflc*^g>WYfh){>-SFtL_v>NBO z%7Q=1iS+}zfLQCM`p1vc-YDzXBNy|DoBCIDxZBaazNnohKoid^2$L75G`t11hdYR< zWFhmB(=haFOzqipe>iI`8sA~ebiX!yUxOWwDIcZF58Dal?YfbQv6wOA-uEzYb!7E= zjefhia4z0H6X-|&A;^QTEdOX{Y7Crp=XG+SFQ~(w_EC)H6k}1_RTOI;Ld=KUhbDgr zDL{>Bf7%Bw+sSaZuHn01cl|AIs>sI(-o(D~I{rlr^M#YT)8$B{KEep(Th{-Sa`=+! zDG`dXt7=V}yjJ)l!~$LbEz6Mr?_=wgQrAK$_k^5zW>8PFK!rPP@0p|dr|0ez6#%X* zwp(_S*cZJPy)3;*$|)mGwe3ec;+`QbD{?%l=*=VKb07t_Dc=xWU2cAp+nSKjN?&ALP2q zKX0?cao*_v<^K@T<3ahJu!4}{nNy8WE(60XTGDRrZ_4#=IJ|F;^4j`1m9S6gC@{j7 zKhr&prPE#p7=``>#dTxwke}EWBe8pl`pIP+Y60ku?d`UD#-bzI2nLA~S+fQ#4UgaV z5C4|WEt!fE7gxnF(7q)EHp@gmx06smaX})PC<+w-DsLLJLilTw;UsCwEbTdhswcCG zp$8>81Tdw7h^Y$7!f79jWj_$}qPF=Io-<>_iAV`Y0QI;vgELUSdOsd2E-S<5O{fgh zo8H#bO1O$?I%yb8>&}2SJ*!P6lSBi#WxUMCqiWY-+`^}*G)ty^rj@Q}RYmMH^WocI ztm`!)xUQf{cox3g%2RBh7YYhYPte{CFc|vaF0;WSDHI()n!9pUfAJKMOly!O=@Aeq zcJB_mJywj<;YewUOd#?Q+b<_871_V@si(YE2Q%E<5|7Tq(LrPQJ$4+QX-k-4Cx$bH z33sI^a@;dsEBtuDSxtu#>5ME{XU@px;Gy%;nXdtieVZE_6OF~Vo5?Bw zG1z8vKpz(66)v(#=i7(th%j%prm^-Vn11L#JWOhcXkq2$5|GBszJBS}3ktdF zK;?seBZAOm|1{$=RZ|RvhQvfpOeaLxdyuaQ891d1lV;^I10_{)<{Ds#-(tYq`k_H( zm9@Fq(BP0C@NR?hGF!Z=4#)***gR=#aOWCqx)T#%lFUi=iHVG3P4Fn<>5lL6eBlQa z?bl2m&C*Nfa&)WFg-w`_2-kbqvy0i^wmPBrdNX>>6wx%suZJKA1Byb66d~=)yfR7p z^N-25jJAy|=&^pD(|{h-Ro&TI6fcY2rf@+aQoju1|2;jt;RIhY{V_^A(!@S^^O;Oh z<s?pV#_{8VORJI!3D6Mvg4igRK3`JR)8+q4~Ag&1Gz5F<(*iE3aBR)94oFI&%wKl1P~ zF-)$sSbLD}xBbdne*|9d9g~}mfvTsuBgC13U)tR%&(Rs3lXW|`sFU+G!&LP?88J|= zIc!aQzaIFG#yTs)q;fOO-YC_t zkTsquO~>QW0KIcAg8|CaC~CdS{<k9&bcR)_5z&6VrF(i>ZXn(g#61O+$* zeog=qqi-r_G{wcv+Mm*sArBSM5>cyTPZLG}BA-^`61#=@WfCNMp1m!TwT-O9%85qS z)(E9iVB&)X_15mm*OI$GFbKP5{Hu5*1!dbG1>?_jyn1jk=SR&3p!x@)0IK_TR=_7e zA@xY#w#U$p=BWE)X_+iF94va}b!yhKkXqPs7?|oo>)rRnR|hl*fE4(ccB2Y;ED6$Moc(aBRcNTr0WA*Z}+*JyaPK8m`yV^iv7pseFc-?zX2 zJUSVY+ZK?4(($dF?$O#mX4W4l?a!tNd!){WcI)&1=IMw`D>}<5(``w4&V7EcwGE;~ zOG?uDk#*kwe$)%+jLks#Q{k+sZ|RvVPN9p%Pvy4gNVfKETAbsHm=p4CaC6p?yl|~H znJ|w+1rZgRLx>?LFN;E$k32GGIfZua!wCxIx|ol}otvf+1by~eusI$7=&O~x!=eDu ze6yC!uwLzjkK|Qk+w}9v5(?F23O}6G17c41R%`wQcFN9rM4VW6pRvF-GCmPp&b_}vyJ@3wJG!V*~8ziz(eVp& zPf*lQw1_#Y2~@b*6zBz=jN9$HKZl9eP`&^2h95Nw+!a8DHKvJxzvz^l)|>V7YpiAQ z`8^|ml8AUIBx3=oQlrd+@x+EB&bA2fUng3fJM0fByUNuD%1PMe(vi1@!v{+#{*9d` z<6Op3VUFxj&6sN`KMNg^`;7}w)JWh(v$ndYbX}()F=7Z?r<5gpXl!9(v}ApBWiTjh zWe92vj+kNpSflt1%2z{Fvi0-GK+$t}%xg#=|D)2lnSr?$xDw5fFM-XGo+TYa!U)$e zIS^KSokLTFea}=Lw4Iw2KvpMIHQPW}j763O1efa(-L6T5ZVTb)W})YNWRbeabP-$y z0_o)G_(5f=;1cc^({FFoB{Z%_#cRuYCof#l?5o;By8ws5Q_)Ro!zj}P>#8C%AVX(% zXHv~n99VeF*@{sKIsYW;p;rhhbb8sZ>>Y4w)-ddF%+?3r4WicMdODy3CQfA~XK-WE zQWzmDKlINRNP7$7v-2MRv*XuA%cp)|*`6$IL?&poU8V(|3=_f` zv)T)Jw;7M1U*`8iur2sbK=NltG;9Acbh6}-_D+%OYsF?rZ0Bwx%V_!Puh72O&jmcG z;P(UYtZ(%H?E!r3J#@OvLR*ZVTBdvW6J3wly5p8h>a=Y8*Xa<5thhKYv0utMYQ1p8 z{}D1==Gjy^DpbW}_^~lkhHF|{*9I6J!`4t_2=ua&N4}heKr(+ zgo(Qn@~X}n$ofk}$P-e1@donmX!+>Y>#_CUUvmG528K5-Y%pbiEm)w5>62m z(w|VjmpFmOALk`x-0O+BIzJ?!O_q{g;But4yn^MOLM-}@9BZC@@{hkG+(Q@SH-pe^0p8?k_V;Kve}ys zv#<|AK77`Hx{$qUzDrBc7*UF1^x5hZl)M?67Y5N9fqNu0aD|gO;O9WQoul*+U1Ta` z121T%=DLH#X6s9zrR&|+yye{(D-0L46$8Ya^NX#AU+wIOj=pJz7pk0Cv44oyK~szc z`UN;`aJuu(bZJ$f#a)tWC#V9QhSasJ9E3Ldan!cP;%^evI1g|Z<UXqNQV`qCe)Jr9by23eryN)tbYa1H={44*pkXsx>V8( z2Ag0iLk3H4?zf07rNL*q*>}BR6yNX_5Ps-#M#9|d3c5n&C@RH!?h?zr{SjKuy-N9AUqrg|;iFD|Ef1(x5VJlKYlFP3up$^8$NL z(KfFxU{wM0Y8%?EsS?2DO8GE2=tjgR(ILjPNehg}7;=`I8t~x0cp8eYRw~GJ+vA+! z&_^(0K1iE4M~$BqXRP)kxLIA^aFzk~W|r6m2@xi|aLQ`{d_$RyGQI2oUR~`pa^&Yh z_?_|lP|vVxH&efK=RfB`)aWohw^5=++l}rQq!~k{Gg_-iZZh!EfN5Jbt0F*%6@1Ju zwy5GX$CySeY1nm`*-$KCV#Rlf) zytDxOX;f++)xfU~X6zu`uu{8|ptC^by-uSbUAPDc*qCX#NYu^S6l(IFNS=rdJq6>R zr=6PByx6XQlRVpX4%z#5nFV(C@s+UsV`J-X5S|IeU3Yrr9P$0R(3r8> z>!1O(2YHZQlGueus1Y?%y5oat@ho>D^^>W9l7<j7KZiIYl*o|U)|pj~*Hr@Bdv_aCQVC*;78A{4m(lJR1wnEMRL zk(Ib^W#Gdkxnw2j1!l55iu0YhrKOjT1+jum1TGR|#<~9+7)qLHJ4`jTE7_~x88NtI zB;AqXmM?DmO6I0qR$#9|?39Et(2cLieK0TyCuKbOfB7GtL`B)W15z-a)ZqTAGq?*1 zW)ol%!UfTg-pT8*Zy~kd+Yc%#hny4RKOe)%dsb5a-YV#?cvdzu=v^z699^}9`sU?m zfjSf7_%wZhfgp zhIlSxq;9tQAmnlY@V-`kd|^fV?DBU*x1e6d;G;Yb zvHqKIexFRU+XVFZ5$>o1XxdO=O7_kPY}qs_xO_Iu&-9076(V{fbyX}?SX+jzLq~`- zyp@w|#t17}x#CCZU_c=)dyM#rd8{9GpS(!Iw+>q(Vw{Q>+!y1+!$ZP1z&{Q1AGJ-q ztsV1*p@y4ql+v6`dPhVF7p@Q;jo+|9h5Ut&0NDr5i3$quKsAQQs~7*Qo&j0^W$9X( zATddtnKj{a$Tl7jARtJ`4ckTxu3pNy53{z}yvNK=k+qro-?qI=AYikFSS;H1f@~}m zS2gZ}Y6|If&GGE0N$87PQ!O$nCW^Z$KSPwRldqC@{L|TF+ny@Nh7;2|sBGA1s?p~M z3FQ&f7QGF>W@Mm9Ye-|HBP=t@+hH$F*`22`*7Ru#jyo$;FpfzFnFPJc#oWYKN(BH$ zu?7Ov?AA^e7pTX}Qx4lv>=>iM-tbtytu&8Ysj^tQj$D=2ZUmYOPj!1C)P;8NkZF(4 z@0>%TLl_vc{)hTj%_YlTo50ZCLjm;#+To^ZoUrG(-pTa3(py@tk4GYX4dny1vVSDa z5u4UTJNWWbN4|?ISV|(rwm<4Zs{CT8&^{Un8!6h6)#yTZ*JQ)+{OCL;HL$Z9fh*+j zY5GapmNZ(6H6&7@Y4;KeulX7 z_nJmXh`vj;1J0{G72CC7ZAEi=1`N33wz2mBe3A1rtIV@AAZFrc3d%MbAAqZi|!k#eQHSM*^!?=BSffk6;IpLtH=OrgrD z{KKbe4G2A>8+aj|Qw zdda*e{k6y*KwkSO;J z1F_V-c_>qGx`xL8w3-9F1=k-1f_YojDq85|<=lV>vAUNDi!f7_X+Nn0N|&JmoH{-E z*qSt65(`N{#c)TziPDtU+d?SuaMs{*;?G_uJv7tSf@y@ zhtr+u5~zB|TX7o&Hf`=aISeOa?EbHy8mcdUfF{LV?BAivS(2MmM`8QgsXf=lO%6sN z9Qj)o$5|{nEGC7&;PB$5T}GjkGW4rYu$0tcc~avfSmMI0RfWrlqL>|hVkyM3D?5Pz zyF-1n6I3Jc{=8E+-GGvIA25&IMNH2K03QuI<5w`-get0-b-bDVw&+-c_h29d{Sh{wRnQah?0)P`f0H>SA0Y0+WGc{yMbyJ;Ywl_5(=pOrW&3#-f)^ zBlqYQc_6>$RaMH zTssHT+YU1Gb6d9Khh>pe3h z8%9=)0TdkYs8HHK*EUOs1+#sa&`LYnr__GndQii z6n!RUbx7(_jPMk+0h`h0f;gZIMj5+7@CJWVx(zM=kfiwzr5AX7%pzz%Hmio`2?9yp zcW6hMQaPyTBDFFK^gFn*n_9jsjlN?KT$MjAYmWSE8sxw5WpfzpghI{#U;eLxF+GZJ zzfdaylHn4t*wSac$MY+@b)ajM#IwT;t1Gf~%F~XHqPyt`2IHj z3f$|h@KXHVv_EBQ{w5+Ovj^1v*Ul&TnI@;5>DHg3a8%sXCP3!GKQSn13>iHPrS=&z z(YK_6oDom5PZ+9M6v28;?59DKS+E}Xcry)-lE$G5l?+DMr`>l2%LM5km`KuE`d4nC z2nM;?GI3V9^!k^?DPLo@r94yIBu6Kg?e*XoW7Yu-r@F$mcl2w*H`nqnctE{%;J_D2O{skh4k+ zshRGJFm&*+vSkDn%y4@{)=)xUy!^J?Xc)w3@v_3Q(<;%e0s*+k)zfOkZ~Pk3SMIWz zr@;ScjfpA3cNbneE_qw`dzxl;X%GZngR34%=XnX`EZC!!fJ~E2->1*QZ%P07&>Xc= zG!MDHYD0t;cbvvPt_y5D`PuI0ju+|OdjM2ki#kU(yMHbx4w%*tMdM?{P?hgs?nUd{ zO3Kqq3pzF$&58Yi_9*u!U0`EWa_LkZ-1c}jAweoAxoER-o#<^pwgp6{!T(Fx{_v`N zDauH|fQX>ApMQ3w4Mef=n#Cq!5O`9~b%3E^ikKF{n)zBHX)IL;dfWvVc=I=x6gk3c zB1YPR;kY=}3kwm4J@O2CsQc@!yI<+3?8B5-rNY^kPtb3dzT2FMV1D$j*{tByjsUO7 zh*zAsOfn|G<>+C!>GhvYYIXP}Seuaqbg-d*VBEQw#jH6M1dvQVvcRwDFkR|&f45RK zTZXj|+MtVL|6aCSQ!;4byCJA{Pl$lpBU;sh z%Up>2(ktY$XHU*=FQ?$|k$ffQeXMGpuwp(rwPQ_J2`n**7KZ9EW~8D*e0#|5_nfSj zjBiO3{API`09H+az^W({%5N!O}HCMGCp3>@{1I{MY>*e5RqUyt1jdyxbko^Vqp%BvEw-ZYhUvduwx zk%nrBKu#FB-IM(*1mXnJ()3C4$!zp&uv|*=;i8M6BLMCzGhtrF3o0>EgL{!{c?v|C zO{dcA{US+gdJ?}sNj)?+Z{sxa2lCQy_LsI$zGp_I2%^K}?giFus;W#IcQiC%_Z%PH zT_s%kha;P`%``ebuDz4I*qyjrk+5Y*%759ReWGX7H7FG|=tXLKFjj+Ya0Veyjf}U9 zRl;c;9>n#R#5neEIS}!(A3YBLeMd$HDz(&J-_r)**nC|^(>q-=<1V$2x zk`y3%v{&j`am!2;D_9wR)9yLQ2lfhGqQ%C$cE;t;5#}$HjSHRy{#Y-~cHN^tVP&AX zhxUvTz=AbX;2+`Gf0J!>)IMcd5|?&|@T%nY#F#?J&hYi8*v)7k%(CVD&dO^K+dq2n z^zMt1Ej5VEc@@bpb^P{}LbKg%NCyTX!g{+ev+ZUU_95dwD_ z{jXkzduB>ukp^}XRL(>RiUKZ*fTTw0*D9tYluoQW9Gs&)6;5Wyb^K;eCan~+c+mHB z7##*vLcA2eE;bTDH{*q&jW@@^Mnx4}WqqH$E7VOc0&vnpp1;NW85mP1urmVY-@X9}*7)QX7V^CSVMyNA;`lK}TIX$it%YJQ3riJ)f zzp5neL43gLO=oYgBttIDiYc+G;>oo%hN#+ebAgO~F>4guaLUo2iqFS2;6I0)QkL%b z^z`m4Xf_iJ^GC|@kVm{9|>_z1-O82y+BKO6th0)z^i*m z{eFw*sgDH?mn=f_b6=OQQbZFPKkLDn)3Gp~8IIfBV%JUv{k6uww#veZK*LM32$)AW z&t{mQd5zRZg=3-M&4?e+r$}vP=#gckNrQqW2B;$w1mDs5=2j<6+*kDgRd3;iK_0eS zXYIWIU;alX`QEiQ<*H}_`eRnC1dy~PgN#6Ht{O4|d)U*ps&X~d<}siKUI~znohE7R zmG5csTFu-uu9+C_UL^bE>gApugy0DpSYRg06xS_>Y?m^XJ3C-@uYL@y&s(egE_Hkc zT|KesyNhZ>|NYFsG{qncx&-UDX`*F$z%Ub)v~=XxZP$hWkxr^WMX+_k)%!Za$>zN5 zh9tej*fS>22cjxH4|0W9T=V_jS$+NcMsFF8{kjzkNGt_NApokuSazoGp{p;ReI6nC zm7C@bsV`_mn9Uhf?@$KelTEx@oZP*_bvNps*z*po8cnQnPYS9_Bo1DSeejwJ!5T+< z5z_RG>v$!jTop+wD^#TDXyKXIb+y)Id6XH|Uiz1kLkS z9^V+%uRk{%C;K7Wq}B**H2|CNzpoKV0t|1vHmRStDXMoRUzbjo2R+lWgh$b?p6=ee zlQ=GEaqKbPOq=ObqWi{3(ACrLheCA>R|6@d$LI~tXLc|gXknF%slUzoM-fS}PGZMf z?saQ}N_{>At{{UOC2%vUKCchr2HM7%=a}Cg9X15W!{STJhu|koIz>~|$M{gmadoEj zC!jA^Kr$wkxb!{0j>xvwzl=}aa?#EO7W(-kuxF#~7mY4Cg6M%f<3z~_*(Sqk*ut!b z0$9$d}5tE{{c&{lpMLtUWY0x^Ebn&+Gw zOS2%R8?lB_bOuT*YXRvQdGGwz&| zqHQsrzf%iruATAs`6%-iLZ#F|@HiCmrUd_J7T9ap*S3guKjc#aq6Qbj(Gq)&B1^9j z?h<7aB%p#o^_QPQWw9a$Ior67R!H8kqICs!X6LrW=sa59h_4x$B@T4)B#-J7bi>@z zI4*6yzr!~>MIr+|glp+^9YVP@_vi`tKdBtP_$_Z9vd(1Wf%>|vCSzDF7mLr=-Oxp} ztm1>~&@=lV4kdngMJ<+b z6)rVHUj@=5q0IL&6oO|z;T}Qh`MzVY;6adYG*~@YN2MXgu#rPxh`#Oqee<7_pwEo0 z$>4`7x0XAAmMJkB^iTu<#!(e1Ggebe92~#z8>b1Vw}(wJrl1mJ$7h<|ywokO)hfUe z-SmUY1!*_m7D;YOim0IhS-Rf^vGjVLynKheyWm=#tj@LhCK>T2cIEwn5y9A!xf{R zZhN0D4PDr@f}f}y>6}Fs=5OhylVcss#3t_XP)!_2&;O96XnNDpVox(k0*wZN*QvYZ zav5H}P5wOC=wA&5o#lsjTwJ#K_uyzA{PL2XB_Q!3m%{=mrOYDyC|Afe0iMm5O~SuH zZ$@1@@Z%@Uk=geVN~rfs{}gnI3fQ;GF&QPm0@*M3t;;nRD16B4=O)3) zm##i4pe>EG10Wi7bWvC;oU!aT*KG@_$sw(QtN*T^-#OMGu5kLrUz?o(xm-n)(Y{5A zRD=4rIX9*K#qy*v3xm?Fyh+?^et0gz`>^J4{vb?O#s+8be18;NIFUPV&egYd(Tw&- zt6meCj#1owhQH+n!bj|l+Vg9j$^z?l;kE-7sYC5*>Tc;kXws0i!(!dmjDe2q1T8J| z3YS5807rThwr<7Bx9G<|`1sYWEUGEceWDP1`}9#V9!i-#XRAqq1|UoV^x;x`_diUC zeD%KO`&)-42F+)7GxK{73_LDX!e54n*0j$i5$=eTY+lfUjlXlU%TliU9_oVnyr>c~ ze&J^+;m3aREU<)B1!5OZ72I$Tq=dv@F9k*syq^M7Qs59APhc$Y2oqV!;UM&YW?X!A zj8V;Ko3>ftqH`zGV7JhenQSY94N>4@VXr{my*-hx)Li|;*5jNs4h4il^V8|5Q$05b z;D7|1xRzLvwT}iFc19ccfBBE928n-p6pX+ebB5wxO99n~5y7hj^ld$>pz*|o7}{DG zwa);ppX^(6+%Dx)uv>dEwQFG|pNL zVJGt!D-_EzRS(6nzyidEe2W24oGr8v;!x<+xVWGVlf(w`S4 zMOkFt9{u?81obORtr!Yx)tFfkor+N|;o1cwJd}!hW?hJY4cjc!-XEIYeRV9#cGjfF z-wMtoG!Y_xs@^xhisFPe1kMi?hrL=;kJ0;m))1O*y8Snj+T=Q_oLwy zm9~2KJW@eC^|NH!aT}QW9ybGVi>ig9mH-G<81I&S2p4hJ@p1NO*h!ZSxjy})ML^{VmGypFUp~!-&RhUgg zZWTNaOALF4KE^gTnYr9fi~g4Jwf_ZuV$e3 zNba+&QV}@C4}O##btd!YE~Rx)q5;gLwe}H9h9R*UAk?4XBx5=;2qNsFMs)*YZEP5^ z5B$^n0s4C;vMH}HocnQKX6jDgoiDBa5osemtRvYTaO%LB1OjLh_C-o2LToL_QD6|% z16H_ImML8Owr+0Uo6vh!^*XvRvxz>w`?ilqKv0b#7r0Dgg1=~i34wfGxt&ic`zxk` zWp&`F$p=`vCX8c_T`GlG+N^G?xezI@R6l#ui@>*!4DTUPWVy_w*F(iy!{kG>Rd9&A zsLZcBod{&~D0upRtHetIKuZ@01m+SMHGaefKS1{9ep^|!{faXZZ1}w9vFFetWbb7C zbq3q+k`JgXNWzO7J!-ouyWQx4@GlLkkv z&yH1;hL76}aaj^=o3}^5uKZX2OYssZsi;=q2_~0HbLsfjvO;{*F?twKPqsDv;_iw8 z*J`!Nzlp;0I?49}xPid%WY%GQS>E}b;sl-{v?p;B&<2gfj>SGnq8huaz0nYe$ovU( zWK^Lsl7_V{aLcC2{=uo5b{`L(xNePSpAg;R-(t^*^nGiEs*lc7CE|H(b{D=gf% zo6~7I4#I_)Up=TBsUz*p#V5l6hAIcJzMzwu1PPe0Uz3DXax*5`JE4p7!dJ(x1V1># z8@Knr6-wL^DkyV?liT$h5i7!fxC5zC6IC%VQR2{|-U{b0sJ|VD;*MKCt-YxwDLZce$@B~3Ypl%$@WF*nz8}7 z;2&cUhOJ>=^Qy|qU+Mq}IyE~}pMfMp zH;TS{e;SZK?~B^bH>U-|p@r2D9(q4IF=bPUi$K@+?$eTYk0p=)(gkLHOcF2C_d##(SVJj`8jU^*{mUaKpT2UuGlpMPK z(1fox^1Nlv6*FyTp-{V85l0FvWIsGZ!|^gA*${9y)%^lL!Ja!G+%0Bb6>LL-D$9K{ zt@_J&dGP+b`Ab?yP#`%u8lfHU?KT7PT{$@afB7%0W22@x9cm|bpU#Ro`7tlGnIZ}E zPbJwmNt}==x%hzkY7lntlRV@T^ND_FV?M$~DGe~>Y~@I-yvp1cjP4ULU!TvPYO^hl zdwPuv^*5^*;C*-97E2b>?_p|yXYloQSf43w^cA&f7`GTe1=8_+xwszOCk~CaZak$B zJ%k^Ym^;u(ocNktgaut6Ue?%Z)PKD(N(Irq9&Anlz1lxm-bcP1fu?QK9Exbr`xx9PHu5?ojVm^*agZ zN-Y@sYVLZ=#=X5zHg;O)T7)yyxGtc52aX5FxAK^_8+KqDV461Zx0;hcxz0AXRl-d# zdLbH4lZow&|BSsx%En)d-`XP#7PVqbDXsh zZQ`35ciy|n5{PGe0_qhK^haLxOA|}en$l%3T6SH@PMMp>fTG;LmU%&|lAK;c z!v>t;NKY+}KmN&(vL)eI1MHiDm(n9ce-;3!Yk6U;D*W7clZ%)-Iug07N^_6|@K7e? zA~Buii-S`RRII~E;ZxY{0g6jmyOTuG}^R91;F)bo8v>8*CMC zr0Nx(oxfTu$qh+VTLPEK!}vM9Nq(0Ywmt4=hqVm`o6v5@+F|_?KPv2Q2$qPHu$V5k z!|iuC-*;~8!glQiPjjUkM4C1kRgQG*x}t=TNBrKdP7zwcwM$NyE(NJ?Tw{nqjP#l7 z&$^3LPPjg>N172&M)~T5vvt38vZ`7P+);0Y$Sh5L{Xq{V->h*<3GG+ize zXwk?yHQ-`g4GmeT749Lu!QKil0)7G^jtdW!gRuFCmA4`{L>8FOenAWjs-gz2R;&X{ zyYl(;Pba;4Fld2m@g}oevFE-s4b{40Fhm@~IdKE`ixLHqyDV~kfim4D3CT&oKy~@2 z?{*wzc=Kf9O$jj3cnX7}+%-A2 zLXbOvGJOo~s>~#t*}X{SaCXU3U4;x~6c|_}h+sG@`H~kwi9>fj^#Y_2_4}FoWGC|6 zPzo_Rdfi-`oHrDwJVHhI*8ZcBdXCRK#%gS!8Up3S*z|Z-X4OqWaDF1i17%sXOpDI3 zpmkUTLvIcibdC;2iG1D3A6gBo|N=s}k0^#b(q4A#7dh;V}2L0!2 z*(D>x8W*ME%Bb(WW+}8dS$@hN`Vl}1xGG%rcYno!k#z#qfBV(AyCnH<_?Wl2Ap}vC z&ci-AP+hYJk?hvegoB`qdR`w0v$Z1O^i^#cy`tvWRGKg)7dxnv9E zaZXxOgfA+u1_D|7yAj+j_a{QVK6C7}zAMnJqrmS?I(iZ>9w3{^thd(Xexm7tun^cs zK(fdrrJ4CEJ%QBQssAeKRD+3ig~>SAPL+56)U71N>WA7|B*p$be$xC9DCvDPg)l+^ z{yU!3=;|Qhss*9m9?UBb-YcrX9s16e4$(|*8Xr<5TQ8;L2bPC&YWd6j5!~FWP2U_L zZbh~XQqTWv}gF&2|5qPkPf@JI-DJ8s*=cTR_?+9$v_(glo_$ zaM+Qb%{6ttrz4)ju!{HV-vXNMk^8)T3yySlS`be7mAgEN3#M@HoX*>2aSkHyJ++HC zBm=m{4qNc%ldv4x#n1XZx+uk^xa?=pFs~S*aPdwKY=i&ik?pA1g`U>;V`?m5^pz}Xn@Cnr|SL(R+<*s-BkY&x?gyOO?M@ZA~ zf0ra=>FuMwQ~DAZRqDmAw;V=5=(Xp>!$L~C6Xp4=4YT_5=+D%4&u@dM3#|ofmW7vBK8UKzyV&D+?_^m$m zTfYFymy*t)C#Imy)bds<@Xe0rm>E{axPI&QI8-4$esqk7E*bP4|R3ql(_Zh}kk-d}2d##K1a=f1l7~dWQX) z`eNwCRWMXe1%r?3dzx1+_#9)Zg(8C%{Drwbh{Iu+snOKjNq&5$G7;Wb*vX+mUW3!7p!i*9D zf#>Cca|v#LsMyY$D=2=RD(In|PW4bS|4IB^;!Ba1U|H3U{~B87!&gp7cE^zL0_>br zCkY>#V?LC8)u(%3E1mlX`M!h-|2!M>i=Ht>_5tE@2H~)J9@Xp5;yYG+JF7r(M;_Ah z{jl4{<>XY|mwhZ2D2h{&+$)Zj87HGC-+Rvk?@-caH3cJ8O~*-2=sRqjR^pa(7j|)K ztYGDW|L#I*9ryVB=U^8gg*BT9v>2n@SB=`t`kE9h@xVCj`cgcl!3dQT#7pjA#B&rU zsg2vT>4_hf>)#E{RQuy{t*3>AY6JIxxg1<|oc1^otqicgvEM}p>}^GDcm1qJv2L&r41dWG$`FyW`P*v8;pYy!mra5h zafMAzi&X#vsUF{`lvO~8v=2pQoc2L`75lhER$uh$ zpm)WjrUE{)L20_`-<1{jKNA*n=z4IU$C_AuHxk!}oHt>IpLfe%zlIt29n7Bj&kYcA zyIHN^UBHSU-%NPzFGjdOnc$R3|hdS;3AhJ6|474eZAv1R<%{U}N5s87YC_@SZ(C2+wpCr@%G*{OzY z1>exK$;PZjwiNdGjftz6?K{nZhjX<4U{$5{U~O5=OQoQt1^D^2LdtG}UhL7!k;k3P z&+NcsQdoPIXR{7C+!-{;ZoNv87W^QfH)e=(=DBnuY;r3 zXM4%Nba^Zc@Cp1M5^tmI{FHro61nxzxIIUZ{13h~;(x_xrtPcZo{BE{2v-G3CRB-m zVnCu&(wYESqizPEZ|Zt3nG#jI4^IB7l@@3n1U^6B^-evx#HKx;d)LI3u<7 z({%2-Wd+f{0($a~PQCoAY@MrYEA`6B_P6SD7#Pn#&RyPTD`dHWZ&D>VpaD`l@gdp`qA2}Yy^4!r| z1$IQpTgt-T5H3G56BlPTC7V9&E=ibvOKkn*`LzQTW|VKYfuG98o7G?bld0gXNk(!+ zx`0RelEA{0CxkRT&N2REKE&1haBU7wor<+4UJpRF-~g|_ZTZOC*44OA^HuBwt1ma5LZ7%XLHXXrR1otLK~L#xd%yOvtuT5xbYJSS-Cq1w_Lc^M&Z+ z%o3kEj(oKtWr4fC;T#H95;BX=C0ZvZ;Eaj*+na6`hb? z6nAKS7gNd;%?^hcF)rCO&xc2xttPS{t4A#Ix-r1t9*J1vSC3LG_^na$D#>PJFe~Df z<+4K+Als3xZ)YTuf8Lq$0U2*(cTNwB?Ms=4~B zTj|b)d3GL>DI)xEmagTg{oi<+^8X+@nq9@ z-Z-2P=QKjp+PvCJwrvS>1K*VrDa3QC_V@JLObjU-@AysiqYX%+thYnwNFTaj$oFP~ z^?et!xec^44rFEvPx9S`1jr-KGkVA&uLwfO$5*9zg^U*)wjSX}7LWp;naUcQHDyw- zcS!h;;7-K?i7%m+S@_9YUCW4DXR7#(dR9+Q?Tff1;ZSqBxA|1|%kU%B4Az>+PHPnf3@Z%?Ypoo8tTlnvBu!nR0! z$wA(Gx#E2&!;Kh>Zyxnkvj2pD#0NUd`02+`mxRO-i*UCJ)no~*Ni*WslTsmhi{pK zAC}Z;yVk5f4Y1eyo|1SX%M{VDB#9IVKXbC{&QMW_-zeY~i}ZD1(y*oVUXC}$oDIooMPD>9Q4NzS5xG8wEVM6HdP8PLl{H?vPy0mL@YuUjernDX00{jOlg6<4%lgGb0Muj{4%gwX+Si1fFFu`o!7X?BE z4m}yHpXHqDfjD^1&09FI+-r+lKt~!n7Eh5`7rmdyS&x|BtL#^^3#u9)w<)};AXx9A z%3UZL$0-tklqe%V4WVBySxhs|qnZjuHTBEp2E49hDL5Yr8`^4knYdqD(!lyn>C}+Q zQcL?WNUbAxt;+t6D$QTPpG;|z`k)azJGozqaT$OlFi&+!`^NI&Y+_NCGb{xH=*{!KIvBl^U_JR9ozH9+(tGQf2Z#VUDnNwy(5Ex^b;hswHce=I-jJ6}* z1E`w@9yhvHD4b*T?Jqn!tx*$w(DHV!<4tGExWWGr$*u$i3xAas$LjvBYi-g{0sBhV z_({F;G}Qd9W?^u;m;K@~JN4t7!YC?h+CZ(rv11|0;V#mEXucwp%D#ya@3=|$EB9C^ z#m4>4HGJ1}ZvBkd#ZR_?2Xvc=uL*+q3v#~$S`>?y?95N)uF1=*M5`ZRX)|r=IOwa= zr6LZjk+I{gUrl0H z{euUB zFNozL1lTJSVD!b3layS*1Ehmo5R>{MT*w^{v|S|%U3hmZR+_qSScB+JxW9&w?0jcI zV_N?2)5qc9`H(Ls(_$2A@FxT1f9_E|>9LdcBHE52b{~4_Gy=iv%~2u3?LW~152PKl z2*LZv?blZp|n~|UgdFPUo})aPpp1=%#hiLtPaTV z`dhW3e1P11?}=AoTV^9wu3(6depJ++pw8nJF$H@*NYTH1CB901CU{_Bk6qFThZ_7psYcCK*L|UUv)N2043_VIXWo^p{I$Z;^D%TbKBL9vjOw0IUq~aJ1(i4yh+}xqB(QX-(#E!rTT#?LwNbCjl)Y zZStJuYMg}s1px5>7VG~v2>c)GDcc66%-t6GIOo?2k35eF?d5P(zqoTFei2HvLc>)} zY4+reNZuHNP|)(JhU>WHeo8@Q>iED&YO7j(1}X=N6dOb6)z9lmZ_HK?SDsv4Xa;NU#5|s$ZFV~=3H>}mhn_V8L5sjc4nOcA zzez|Py9n+Koo%0h=XxkS{8q`2J6b*NzOGTmJm# z(>|0RB$A}`w>v)Yu83Ms7I%G>A2cCHXsZZAfO${4!G&r%h=?w?ms+QcI#{Pn6pOA z&J2bP@i_8QwKd@foPp0)ztC=ER?aLF&BGwrnes= z&e9nHX2NL?L;|DDYzQEcu_M_d6-*sFD>;EaTAON;+t^a~W14F7=N4MwYEdPfdD4K_ zL`+e>K{MRPF@I&D=2-!uNw-E+5QGzXh&l`&1r2Vjux~OJn3v_WUCSF@5S3`cIQ(!C=;bm(GXktEp?&nH@Qkg7zmxord1OVz3lBX>O;y>wrYz1{Khz;Fm*q5H_bnd*PZ^u1cw(h(e(#I;>Zl1-|_PgZVxq zLO}9Z#ghcQ@aNbzw4=jpcF*MOg2TL$cx&d=pW3nHBOb4(IMM6ka%}S{tX*g0k(jol z9~$`JHf`pzhCa6HiE5jjh5XRMN9dn+u=m)7oP>w_4X9!`7WdGVnlL9{A}qv@iflqT z8r@UCuiL@#GIXhsAQ0*?e4MNcP(|n+>$%!hu}DIb3;Yi74&kxwSj(+$jhh0Lup>g!dsC`#!{G%BVScc%6)==%AMi0bI!pX#&;`rT8;d0a z9RA!+lHb;XhWM?s5x4rk>b7PtYzy)#+cmT08VLCqZ|I-KoQTc-!#1m?c!gODtOYq- zObGRgZCLF$mzN$FL|{mLYr3KWI7XCPkfhG`G>jYh zp{xDr7(tNY!R`AFI`p!d?Wn^LmnC&e06sO-EJ(Zo{v;z_q}F|JddIezHg`&k`~{yE zof`b3A)BQc*xmHXJW8=Ferh=bnkX&Pzmx^z5*!1oI?#i67#22*pVU6GDW)Ro>hkOB+0}0$C-&$H%4!eDwsTgq z0$YP!tH13e@*$5lEGbs=Qqqd5(qVRf@7@-}EsuKFWK*V1Y7UtkwLlb|r@ZGsddB7n z&krjJSbgx@^skdkJWvIRUY6EXYANUzY9h?VmW+;47IAut8Yp7pZUIh)(R&rqZygq$ zs+yU|LZR2@!#$e|rM_QZ4jL6=RM6JDIVSy0{-m&uNl>zt-O{(k!N^mULI$&=#QJs8 zXgs-prsGvOzcP`dNa{f{RdP+*TOigGLB$9V|1A>jH0s57h}Y6@jquV zCXFx#wC8wf3plr%27Bx*p_zJUT$1Qt4of>d^7dlfa+S=O%u;V;#*|owy$? z0C2ytZ|qa)bbz?&Ofyzu1D}0dJi~2(VJfEg(Jv(?bB*{5*|Xf-4%sJ577fbTuShwa z@I()$1WgC9KJe$Y@}G^o8isjdFeQC*D589Z-E_b~q~4o{QDHPPqg8)?pK*;AJ;&*T zo-HCaP8;&z7Wp`O&ADDIkPH!O;FZrKXFiN;b!H-Jv{Vm0xz-#-7(S9}D~9G(t@e^N zdH1XeijYDuky{51xsr;OLMPcXdYmUtX2`mKGLz@(>^1>HKfOfo$TWfF14@kdcUmI2 z6Y7eB+y`dWM4IUnBhshJJ1gtpz`7dc z8yoJC02E36@`sXH*#3EnGUlQUZ-3gSE5#_3hapT0bq-QGN-Z*?BlwxGybAQrf3y?@ zWOEJB8wZ_3gs>^Og4$qks&B)qIf+Ir>I&-~(+T#FoC3~jU%($I3L}5``qr7DqbB`H zx0a|kGIxWoWd3=n?Q_?C6mq#zT!3AcntN5Ip-h34kexw6p@s0wYf?C;;In_W@cx6R z?2*RkKU#iX)WGkLCd|TF%^?jW-BYZA=45i|DWkQ|9R^oGHlqI)%}P3kPL%JR4Vf(= zO$ARd1{M{GzruSdUbCONmgU3d-`aupycGUCBjA(xruM@kF?~t#iQ>TFYD|P3APL<5 zv9BoYY3#@d4SY)c2U@@=^BCBJ3%#vV$}yvt5Pc%Ob!%^Tm_{cousuO);DFjSK}Jy_ zocBSer1uTwYh?^YJAg}?{E!#DDRE-%C%zmkT8KyGDVKtbweXQnB55fEAHRs2u(Wza z>HMQ4e;^iKc zc`h(L2kwXpvNC3!2fQW%=4oydN+#hxrFM(PJCArRfFMOT%y&Y*Uoq2RMo4hwi+KUl zIJvT?7hV{ujD5s6NhpQROVy%uF-snYQS%8dCJ5VP7tcaCnds#I@@o6&p#_XfjaN87 zvuMidZ5?TXjMBML?as`M3A0*|qXg8{{&QIKfhHo<9R`*K;#_0k;s4s@D~oLUMI<;xV6TXKdU zoWFZ>-hQUA{~#E$KyM~Bq%!5l00;~2J}y!E#9jhtfdLzZwA~sCK#pYY+=7yk<;byu z>D?u~7Ou&m7R2?|22&8W{r|eG)D$K*f+nnP)$0xF zN!Am7w@GRIt1BHdV&?*RO(m+kae=A4+bomYi_gZmuyk3khtSv3xmObDo2Kk(c;C4M z>RU23-+;-Z-GcV_dJY-8&6( zom!9L=IK0=f>u6QIt$l;IHAe9OE+VXyHVdC60>m3Y3t`6A0}}&sfX#ne<&oBxSLYh zxw;fkmBUUoH^~Gi_1c?wnc^008atzvVeYKK_}>zhYa}smmhZ{!jt{9ppmk4W5O?rD z#CJ8s5V-6{j-|p3y(B~zfA4L1i*_?;RlsKI2#W89f2ZOnAVsP7*(S)+)qK@dVcmE1iL6Fd`d0ybn1=5&L?V6*NdG>7bpOH_mqx* zY_7~nPUweB8WV7N^(+en$oEw?#wO$$V4#kkIs93K6S>|=uo8`uR4-8^?lyBfTRLB$ zoqliYiTqJ4Ng&4-SvJ*?cLBX#HXvJkfv;buj&TTCJlF%1g(MMD|Y9}`USsz zgy6>C%M&fZm444|43PI{a_bexo+|D}0D1x)a)xOXR!edkDXU||jcvo#{a34_?kN6` z%~nZ)BLWXmu<1`lR!qOdU3dxa&0PXIN8Jt(_kN)nhU=OYHnfy)(hoOD*dOD*%hHVM zlIJieCUS6KU??A;Hi5k{qQ0%TyZiA6z58!FL~Q_uo>u`b8FmO?N8Tge%$b)5SE5f; z!-7x1WzCEB!PeYex6XGEj(u1lZ^%dK&0-GYRk3>wv0{{*$`72e9R>SZ<<}RS4XrU> zkw5BYD1Dx^X6_ZxvRN9>(}4jW7IwR+LEJ>EDK{UCh!73#RPvY}*vSw|Kls2}19|M# zQ6`N&+20VZ#58m#2oE%0qM6Une%(n@7wcx!V#}_~T_g#*e<`z@FB!znw`}1C0K~C* zvzD{u32A?0A+DY{;@CpfVVh=$eSvRE9V@FH8I=_$)Vw!BU)Bkr@NQ+s=?K4j8e}3U zpM6-mv1yGr8kuZUgwSjLramD8xcTgb_ym2Vb<6HdbS8ajr0k%XD(82D89;6Iot3)(3I|f&nKr7Jw%4gB;TJLMrbZoh5;qdjRH`FtS2$5*WXKeul>5cX2 z7pGifVJA0D4b3;QQIy?zSV!X9sJ?YrQ zz+HNVW-f*>Y&~IXM1>CIj|9*ut{$43v_Hr2|9FPe!aU@KOu{{)u;iD;}M32G!itNbIOf>R5IA1?BKE+-IjhN08wuz%6C1XV| z=R;dy!9ad3x#PrVQCr7!ITri)Gk&HhY1#lF{LfiNJ6=b=i|BQ}X^9(PP|eLqSP zwK~lBF7RDR!A^2JBWuX)7S|L1ftzm#%bNFp^nbq2q-a~l>@(@}w-WS<7A{cUE{-vc zw?bsG;I6#epQPiiu|DL+xegFZ);_6)>#rgzIPpmT{$^v!NXo_S?^Xbjq?P)6`Nxx4 zC-$h(2o1e3K*T1K!-I{#HWI&_-aX_cGz(lz?&on)BB3GKyRh7zDEWv zKXz)J&YmHWbz4DvY-lvhU_8{yE8%7r@qBlRDP=(gQ&lvvU{~tRBwtD?$+G-NO%Jv@ zW0>bi>8e>q3L0_1Q7@y*6+``7slQSEm`Vms8NjvZ#(_bK%uVLOU3ma3i{u!fm$P51 zzz>u$#rWs(3d+v;EcVhPvfyYP4n9a*A=?0~&RyvJD>80#V8*8aJdGmSf2GotVq`k) z&t|JJra%31sBH4XthJ=K_>Foa=50A6Oiqp~Nx?DX41j_Foy=bB6H8XA+^WC3D z<{q#)AEOrUXv{FSK|9+6J`$M*Uh>$&zqkwiO8WWT`A3rPe`j#Tqg;q_{}thS1*ejk zp|L4aZR!_HFq!do^8V{FWuO5W3zWx*@Bzao7Ha}oX=V?s&WaVUh>R7)x3KMq0BZWO zV~g(IXgN6i#m@54CyMEjtXbIv*=}=@z-okffER{oz;z$!H;27PRE+Eu#W|jZF@`Xl zv?0~hT~*$rMT~KKv+Jg!LGnv^`+cL6KG7i7e987VMbj;skk0$Z=Qr}VqF{ZqGoT%p(8n_vfrTX1rr*Xb6 zKk~m0^8d$;))?1?!ESZxK-F0G?Akg|?2VI0zvE9~HZ1F38Y|;!bJnHAc4Ie96W$c; z78!`~L{SmImq@pzitP#k=4v4;)tW}+V%=K?5BmpE-xRP8iy$`FN8b0$hvGQ1PMW9; zwC|U%^J2$$?fNfLT zD4q5PT1m|}n8du1)br2O8h_h1y!VAc3gWtp*DRa>wzRW&?-H=MS=a+M3{s(gOnb>-U64x4$YPt_H;Q&Y-w)+(_~h z>GB=9MS56K#4(W3iF$tAQl{}v7X-M(A`IKJEL46|=0**2rSWwLEh{nctn#B{uyo&H z;6+s9SIhG>;`tIlckMULhm6{EJ_$GM{+w}a` zV^2<>t&IJ5XBVTM8}|*YE7HCQ*0IOH_ zoH0L%aEB_(U;j5)az)4S&pP6o<5#1<)Bf^G)CrB0+JYo&KYsV~Anc@v*SJ z6a`;1Z6NJJxB1!$IpjjEH4B5Nzl@X@e=Myc>*I@NeM=FLziYL~sT`o?!PAL5en;eFX#q|FV4hZ2i0`GRFR{|Loa>^S?EbpPd%U1goG zi!)G16NuJtYfM~%Vq;gS57Nh+?E5=**n$^lZEy<$;wlc@FVr^lTQG5H9AObst6x_> z9VF%PM|hO3KcwwjF#R3b<`qI+;cqI0c<6nP?etd_LzAvdR{=2`|1aYuU3WOxD+gR> z%71*d12&dc&a{4~j$HVy6mdfs`axY+_-Dyx9^jHqk(9`PfYLRUK^oHynFjPD?BS>a zLYGt_AS#OR>`|-YxAzx_?lf0tW4qoKZF`6%ne@G-edsJ#o4Q$Y$m>h^1~r-9fhdsj zv@WCqBU&zHI3O+lufGer)}xd}`tbNyHjFXNex(a9t1tEj|484DX;mAwa66P4nlm08 zO_s;O$U8zrb@(?~&taVx9?U%%&Q>;Oy@WLJo5I?~TIOI5vqZzEKr`ImH&N z1L~P6Dsq;5Uk3FnG|!UL0tzR_O!;Zo@H8UkGbVv`v_HVs`%2YDWPYq8OKDX+h~I3! z!tD^|^7h|rwYkla%8++%j6PL#6A}RdW5(Y+8|P<8_oGD*d*Cmmk@V)XWm{`5?*r0e z%%}Z~9LNdCum*Cz%(C{u+U9R#nl;3c9ig`9ay5>|%fgX%6qi9*iZ7)^)2>naw2*7K z^WkAsN%phE!7@4%NZ9Q!q;LpU+)G*kr1dZciYX^jzf<$tFa`Nnex zG73LObR-dOhx~WRfEmSZ$$!prUvaSeRAHpee>L*Fb}}O{$d=Fiy9=5a3jV!_dZLT$ ztIyxm^EcqHD5gdInoh0*v7LWfft~nQ&L+yb3zkHKLX+T;*6Tx8k&cICdAg^GqU;HB zL!0U(A`xC+u*vx6l=&z7_L%rA4ZG} z%qvptrLno3z2<0;JHsL`=TgdEn0~`-08rqo+-FG!3T`+3J7SPhbPNCnXbWn_xqfy9 z_^Fbwqf7k>xb3Ft4xnWtGNx0B&hUC?JwnElE0Sva6TD7{prL~Uc>}thb>u9@&m3#q zyG=)A6S=EGd8aeuLRdWzdP^ll=ee#(;Zr~T zW*)>NnDdt=VZJA@D;>%5GRmkrT`CCDbgh%l0a2L2{d{!>Y;Ss+z>Q6*x|CWSkT6%r zFER;ec+zeFB!;75f==H`8pXL-J8^br9h}<{&a&maxR@gF??0_7i@G|U-mEou!9h;j zVwA4j!n`(p%0JE!fS}uYCm0{(Od89>vS%va6_hB^K4@YoIx+c}SEJY7;R;I`atQ^hiNkQ^Ag?$|MO z^xy(nGuD3F7yyTjJY=%wgkNv8{0?oUpB`kFK@}`16qi<}^`f_0?Roi@nh!Iol}>lJ z4g|6{<7{y`G?%W-q*A7@3z3grm2gUc$afa@E;4sx$<_F(w|+;igipg-E(?#+k%b0Vb-B$MIB1(Wgc)ctXi&a1z%+@Aj08v- zOQ(tMt|SmRw&#P>wO=OQ&D(Tw@wc#UjgsY9iK$CT6&7>mtYWJ?^PAi+&uX)FZ3LZl z-Z;ba)6;ecgf}58w-nsKwLVEW z(L;FXOH4qK>bT~(UB|}pyw#Ti$c;8%f*ycB7`f&xXQ+koOa3Z0IED!-3@XiToXDjf zDH%0HpzVHc-~VYE@$fHEmhd&-5SRRmO9}JzYDAj$?!b{>a?FXM0h&%(^O1)&TPm_# zREjYKwC^f^c9uL)?BYL`*5`xdCUy|pl zS$Qoy=a1}C+*lsOl5NK6C&&Gj!3W? zFB->%^n*^jf`_*otz2U4(&+@y^ODU{ZvIT{KkeN(!*Yf%@yUFS%}sYbCTC>B*LVMg zEK#J-upjPLsCv3giR*OS$4!7(?fE~(`u}y=CL(1{fsom=vpTytMbj}#bs*-b*-%n+ zAA{3JhIcc+r}~)tjkjj`g4Y~K2N<_+C%w~FZbHNt2>&!9hLJs_{{HLqMraYV+g@)q z^ApXAx7Hr$-Xid;Cgs-HSKD&fzwD@bqb!hi9D5u~`?cHnrqgFR9;@wOjpLERNK}+I zkC*1zXM)Co5!wn0$J8rIedhdQN@xu7NZ|KSn+SlgJ6(jViS-R^L9Sd=;%r5sr-!Y= z=rVJ(Y<47%J?q^0Cw+j(R9o6r{Je5}+vSRdn(A*P=^vKQTf9FbUsL>%ACF~lN0`Ro z8)mb;A*4C7H_d#=~OFzThRRQAN!?!2qc5cjU2={Hbbk{3ts$_Q9(WWTS$C3U1r*%Qw-Xt^`@%-4~G% z#Ot9!x_{)r)@R_wCo$im%rGh(3#K*cudm*`AkJp)ii}_cO#%tQxGP$;_Q@~jO5M2S zLD$zwF2Z}w?y6xuth5dp;R^ZWBOtbLzXS(I3AZ`pt#7cBxWd&~HiQ`M6QuBUS^`UX z0+RimRMl$kzpP4S{zUrTVW3MTY0X63ZFgOtb|})5YG~(pEfKD;@myYR5%Y3IZfVow z8pLAa`1CDVv}v5LXvw}oN0lV`KHXl;riaZzaw zW){+w5}88y8TE~7dXd(<_PR(&rLIj1uUD`R4bR#^&7$?yxU}BMT^SGycVcVn#(5?j z95=u8TmC+1XXd#%Zi@PCHyJNs1lHv&?ZF-i)tQ(WLEJe~Hjdsg-9yda1|bN&4cY`+ zK_JKS*0dC}JbO;x(XT1?qd=4@?qg;lU{H5#L?E(rsk1b;uM9qEF9FX|gH(h1{_%QD z$g<4$AP^)ho@6%ZIRT+BhfOM|{Kz@OqB-D%ksgir_!ddYd(vuX99Mb5fmqtte&I5< zJ+*cpmU1;UYbI(68OG-`zc-C-B7JL0)>*{b;|r%9X*ZET;QSmvMP(+Ee4C=fI>{Zv zC{9R%Y)jo?N@ZIgMEsQDI1W|>? zC`02&%nN30#kt~o1ATyQ;s>d!xxc(c!?NIfUPrw7=0trcUsp2GXO6wV{!rb~cx?GC z%+74Vdeo5QkIRS!^6thQ{4VHluPB{YX* z0;LQDoQ6|PmlYeKn~sbiN~DX?#X_50+I}&i=q^FOqGsC3&dP<}h|uAYqCa<~{NdH1 zbGbOD5X=Hkx*fp8J7Y$8ePlO>2-}e58vFU?d?K(t>!)`l2a&H2tf-6RWEfw6w;|iBr zK|Hcau-OxD=hn22NX8-BBP2miylakRyz?(p_)FpbJoCL683GT1;;!1_O>SI}^WJPT z=XS4DjS5j001iDjx-wHo(UBP!~jfx=5@w9n1 zNDls?&5u}^*Og_-RZZk`c;9~5q&0k+5Ap;`a;={UY5%y`H3kGqtnLy8{?y)&^{ zGZPLL!djqpPl+5Y%0P%)XJ*r_fKGxcHlnt05w2D?U+r*`rydCLNVGxg>`yH;=NiNG zvd1IdbLbfYI0ini8mb24FaJC4mwN3wjz2@99aIH(PGq(#kGVz79m;KHN0EM=0F~x` z=f~C!i*8Ou%niAMC43}cN{vSY4Y@s&M^X4h3;pd?3vAnxiuo5BdQ@PBzn}G51oc>G z(*>D!1bpc}hobWaoxD9|Va(so>Ap}#RanCSXXd%ly{~dL8vl`%pWMZzfTW-Niym=xbX0wsvE$6~uTp@R?dv(&%ta$X}rp zL35KKdRiFsE}M_=QGDa7wK`U{>U8*gQb#ycAPB+2Qm%QBT^-%4=1E-AR~4C#+dHS^ zxsQ;Bm+u$%N@jZCs%V-!8NPyU#N!}n+{cN9RV3^ne!BI@KFD;FrWRBEv#j`Bp{uB? z<{>6xUW%*t;u3HEKeSy#lOWu(Y}>YN+tapf+qP}nw%t8#+qP}v%}@B^M!bF2>TI$q zPiDeMIQNtlYt^lcuX$iqqu{Bdo+~rBt{bG45+tsmI>#mwNp^!aOZ^>2CSDft<_4}A zwOMhYNi~y=mS!H&aP+m~W`1av=#)MjhQKAvxbs5YC=RC-9_GwN49f7lU$^x*kyuD(d`|vtT$+guK#)a$oL&_2h`R~U|{#c%t5f6AXARc1e09N1r5@^0V!}3-ED=e`o4Ws)uGy%3{&*q{#xvufN<` zx;V!FZ2bgn2Vfy|4#VQx)_R!QX70Ufu^~k@;B123u_lq)H-Mhv*uTmtPj8DBpx|Mr z997e&GijSAHRBRnUFY8nUbDvS|PJD?MjJgIj6DfYIg2F&H3R@VE4#~OuI0|+nq3=_D8b`wa7~`uv!|RMJ)HbBY z5XvdtP3Tr3b`eQB<>qd+LZMBoI&+O9*V-4|UFr&?@e-2(+WQBqzqjRAptbkm4=Q&@mcyg!60jSyixbq6cfa#Y z@=R^RgvYHV#)4}&$7j#52uj7w)H%+pj-KuH{3ebVg!M7PtjE-z^84+r3M>QX_w2dc zquOjm&!uJqp%UYvf4xAvnlvjJ>t9Q;a1aX`=2`!x6MlxWhCwhjN)w0@A!6ujSzt10 z#i8@eUbC1xSRZSU=Z?)KZqDQPqI*{1GaZ>}^IFfR|4iinN8p)dm}5|L598l?ynh-h z)1l0WU6!a&A4as5JL(7O6s+WiQ7qVIb-<_n#>Su4;>Amn;+-EXHc@-Zj)+U?)44sZ-GT~C=NoSDiKF5=$W*L+2KxQhn)RwC{{^QF9tkw z&VbeAX&1H6uqkB;b9ZN;)A)prnUub#?;+_i!?3Epu7sL1$QiN&NL{_9Bq@8|b{zZ- z!Do_q$^(Rd?!$fWTfR)}ucj`e8znCL!T{mdeqy$SkPdBd9jtHcZ_ONt~5aYK;9-*xtAg(1XV}0jLh$c&3y;Hmo2BSj;=SUEhEaZR!qi z`B`bGR3!|a5-Rv~kb4^R=D_~40LSpvLN9IEn^SIip*{2RdIB^Tm!~o%zgL|Eu4_bF z%FIvreGTE1U4Y+i#`~@S?CVmU4s=w!3?jqF2~6a!JAEn>Wp`mcKiUjb8oB<4h&xB! zv1M+@WZRr^H}rT5*0y{3oDMeXyWB zHoOI#FgLm)wdz71>rjlvz3$oRhkSnHqe8VBVUN#T_5B&`Ma@-d5TjN;|2 z=)WDrS$JMjaMW77e+O|K+cbUvrj{2tH~2AAPpp~gGGCU!DTZ3U6+%!KTso$auH zJYiLG204#yJfShJFHUTEj}Z65^d*2o>$Gf3iyGvNnBUw&IXxnyAxV`F6^F5D6Fg10i)QOnsf?rRyf){2lA~M{;x> z9|Gnps42=4|DrTv>-9LM8pfwudskeV)3X7tRQN`9EjHMLJ1!ZW3hv7{H*MSJI(3Mm zAg3NppNPKFSx+vPsDrHK(JQgII=86_E29yz)4nJ}AS~`QSN}ES&uLX*MYM;ce zUK;QHbcPb(?f44C* z$Ng_Fo3MOB<`AgLLwnJXmSpbZ++wr|jaXJP8*~iN55S9Q=g1{+>J__6 zqO`$F%gCC7OW*x~&Z)bQ*(jf_>Er{ zM%vF|j_;&R&--rT3Hf@KY|^oKhF0(CumVB47L0io!=6nBLp6CS=wsM0lQshAB8(5M zqCd(AF%(muR_$jmUSn_HPiRl~FsjL@k&}4D@LFnwEAw~mU{ZS7)QJ<2<&bHnu*YHN zn``W3JyxvxpgJaZo8MKVWP~eDCA~?y{u{o1rK7Gf1KWBV^^H8ch+0kPtsC}~?=&ae zi;Oa2nUAWe>k@U*&vI!LAo;)uV{RUDGISn)mE30(d4OBiZ_;eG(MA8VE?V#vjBEWQ zVWqDFa4v^5#j?Kx-42L>Nj)DrUc|j#5J9?@s!%Ke+?nEB9i3+779u8-Xz&F5VI-R* zf))bk+~VqVm+|oe_al2a_icY3upv&!U;a%A-?RO;cNLCgbBqGY^sHLsO@Q-TX=wzr zwOUr+poThK8iWqWy1`#OBr(kRvZAl4cj+#rKd8U1Ib`)h)LQqXzLI$2hoJ&w|CQkr zL3=sM3(ImH=d8P~QPWEz3J6K6NN=H6qtwVscjcWY`SW-CKHN@k+Myi?_L4(lJyK=s zhssG-$cGKIDoNlJdxx@ynEN#Vu;ere0B5FyMi8%e64XYTlglj%rGF6(_?Tl`Pin7n z{%7;@!g)g*2zr+|v1II(ymdKb>(!UU)dzE*s{klV)17`lD;E*7aRq>0t*wutPTGb{ z0WWJFIv5IF!Ls1bY&Kki*S=OZ&8)@t^`=(D!;|#v=V#uzHmh|IVd?(U9!!juQWE;c zt5a2ja{c+ih}TU&7u8zsqgcD>T4W|7tbX|HS7X_Fh5@LoJMK*D>4U-@ zeJ-Jq(a!+x`?53HSQnefW|uE1GZgBC&>Ny2hbG1#>V9WEwkkWD@%#3X z9%Au2%*9o5X_C4rfg9y#w7{;vxo?p9XW1U&bNdf`t6~1G1;&D-uH}R%eCt8@Jfj50 zRhO|l^X(!0IOHe0lk4=y?W9?brpe^_DBJk4+bg8A!4ywz>E-kf87w?- zmJ+Q{Ho9h)t)?mi-{EFV5{KF>0PIFbJyn_vHW&xGjr)?!lFO3(3^|Fv!6Yu}Ul8mbt&MoKk2e&jlxS^Nyw{ zUCR{eH}aWZWI(XuRm60iJ<}${pqI>uL4H|F3}2o_v1~#_Ao+nN>3VkQ3h#g6!J)p8 zUP!WX#NiDetow+%eBj*`=X`mxel}Pzs=_u4x}~xOxcdkMiHkMvq8)TNM9wV0(2dgf zjeQoVG1XK|$pbeOubLs8EJJOd z^x{h_-(xCkqM}8z+L!A42()hAu(Sh)_NZ9xxmRs`eJqtJnOyXU0@EuF#CC31TVjhE zG>sCkE8_z-u&h^JLf0FKkM`WrE`1WWb_OMOc+7HVTd!V&Y1uvmoC>f7^kTuY#bxp5 zOQe&Mo^Fg8!Fl?8v65D~S+R`nowrT82EAiTT*>T*jn$WXK=S?!Su)tDUDr{Y$tkP< zkF@?jkHL+cR_QSh#kS;GLiMNL1GZwJ0Pfkm^B1H~(9uj`HHyD{Bg6+Q8$|I5%x6~s z(yT(3*fHo8-RH&L9UkgAyegKB`+-{zMC6OZ3jMH>#yHXaC~oUq`E$zIJKV)!gUrglfLoO=9N3{hr^3K0fkmjr6_Ip-@>EekXY_$vIib~Z zHy9!^rvheqTsmB>CWJ#28<4x${$=Dv0Gh^-QT;>n1rROY?ApM@Dzlv_U5Gvz2C?m! zfwfnZP9{psN)|v!YDm|n6N}9 z6?0%$Q$gk4f}Wh!Gm%UH#)&r=GXkB^L?9n*5B(?K-Di1@T9I-CdH>Ao;T5XDZYV!V ziE^e&_(z87 zP;jcWW-V~T8A?sLq?aeMW2E|dK&^|+I^sw~C)kl5FP+UN(>IJ7bs~;V-AlyARHB36 zOJXC7`$Fm7!CP`{QcJm1TutXobn(W7%n<>*8^Aukdxb#NQHm;A$ zUq+w?s~e{gI^ban3jVjTRT#ZG*x9}OOI0#N9c=yPm#uZ<_H}OrK?PYfr6*lA%=(Tg zw(1L5ko>MR>x|rpwouOF;1{rY04wGe>;`@0px#R3y3i?kw8nqZP0vXE>Mu%-e_w|% z2Nu2hbQ$0^qX!hj6b(40ZYR9X z&UuDJf4}3hI+WlM%lZ_brGUX2>Yz*J?RyV}WN2+~WB~wTkpAGKN$#|S7_AqA+G|(f z>e(g7Ra|XjaIeY2?mD#Q&w9WVQ2=NOl^6f<@o$e*ufDdxe;9pjn42#`=J z$Pmid2bffbU`pt!9N zYYm6u@YpIs6_l5=9^=5J&8&-sX22d&8O>Tn2V28I{99VllFv+z@Of_u+&eQ-+!Q;} z;qFfn0m=e{WMFp!ZIl@(L#na(F+z0^n+OVxDLm27_r+{zWRxv1RMCMZz`KE#g0zDa zsP*13F>0XMz63BS(P9@fUXm1(7T3E^nYWy$Wc z9=o#o`K^UFh08U*r*s+`e0{H(PH_=y_-}VX8~4vGoSn`XIwx@jD^xapLB)C{N{+%I z`f}BrG62zas4|^yYGzaEg8o)YDS#?;iZAul=LM@#67!Q#g!=GMD0AU9hU^hj4KvF4 z_<<-+xAT&l$kReg{wxyr41<~yan_be+*hJ-4D_0&HbO^K1`Mrx=mEZs$ib! zKb>icps7ya2~c_0D}C!)IOkVP-k7Nk%a0?9n`G~NuEXCWUeL=aRT%2=6w=+M>qP(H zSZYm-L7B2dJGxni!ZCJL42s2jF%2Zg1H$GlwGR%8;!HfFUEj$yL`=N0)=q5?rToo` zJ$2^f&Fk*6aZDrb&)XW)b!Ai9F_JmZN4pKE{BH6di_=Lnf(5Ez^$>hIf2T-AQ=AB5 zV6OdrSIOgte!fN*Mm9~g)*qw?Z+N=###99RE+7}A0#F$}Zm(0%27kUUdje4dyGNeD zutzM^mawd?K(6(Kn6F)=u;nlSe*j+DDBJz{CrAsM!kq0Z%ZWhvSAhMZk=7c%f4dsd z5tL0rKR18eOJ7SB8&>aAfj~L7sjAP~%JYf>T;+WPkb+g;*1>Ve)aKAvY9zfXMsLMT z=)ncc{~0t8RR-jBXW<;wYg&GP;|)1hVxN$`LL?D%cQXaFEy;-mvy%-12TSWMc@p;{ zq?){lEg zUL%6aDXKnMu=%7?fpZgXOmreq=7|cS`$M@!s1*ikTW)C1~yYzd! z75ZMlSxRi5)D@3=vtoMkaTFJi=H;NTWO5q_jp5J}%5P^v0>b~Lmn=o8Ri+Jep0d5e zRkW=qUogQtlAn|THoq7w*4}a@cg_H5xCdBQq96;;ZC|VD+ffvfyEBgd1ZiaGp>)e! zz_gK0i6*0yI6e+XRV+;M9*{l}Wzf}-$~du=uX`6*3x6(#7((cPye{HzAA4 z`GXb`m}1AtNwc1)HDpHmut4aQzDizjQ~NFvhTjJ{6FyC(vZDJdk6`DEUHi9cw;MAk z@O{}KM{CbjOmEw3!N{>Wg`KQ*k-Qo~f$24|SSOCTFey3Q42VO|BNn4 zU{fTN6Qdp@z;5YcW5#HaH$P+_w^TrDwkL{3y;88JceJ_OZwLMU$-zLHF9OR>9E+g> zd{QyqSRct~XR6m%CM**f>m#&NRIwYm+ydoKf_BOwQ&8UwW-#+g)?_{@PRrJww1b>KeDDr0 zd2$?;$a%8{jV6|4~~TK>A_H_$NNgPJ$|ZD2wI}}^G^PKNIU*IJvmCN1!Q3? zQQ>P0<#%jp`2p(({>?I5SELN$LhuyZ6#B(!08RK)H1dqjJUA4BK7XAsOwd73_1=^~ zB>Vo_(9RiF`hJtF{2+h&-3ahs_lRYuvb1j)myj1${x60$EB|rxwI3oDd63%8-_X5j z_$}@yeT@Ayp}6T4t{5Pn&mVbK$}|mX6Yb7V?Q0>*eI_DU|MWU4Ot6XiTppp%yD0(g zd`XKD;4ttyK!GyWQQ>*nFuJ5|v{b@UDKUZ&I_>7Qq>n|O`)xDqT^Et8=#_{?|2PVv z1?exbyuNNUEO@HmrEJSBlpz})R(!b%_%5e8c}x`&1p&QnIvJ8&t)%v_#@iOV=AXnU zz~>?W^b#uBP~y&Biot$+y^Kt4ZbStg>dWV7ll%445o0rKJOE`9cQ%%Dz`T; z({u5mriOpvSpFR3lMVu0fw;yn5#k%0xo}?LC1RSn5WO&;HVM15&3JH-A&g{ok8Q}6 zzC4Xa8M&itaLEYOWGtI*x@#%lzjQ3SG-zj`@yi?dh>30;vLYz+?!sn~MLTQ2N70dv ztj^p<8cQIc+@*&{UR->gI?PsJ&M?Qe7bB|;akAI(sTJWZ>CA>Mt6bg+5)wE1U6YV< zf$vw)rXjm^TX|>eF5w1iRu4`C6}_+=2SgAm842tdsCpv>k;bJ0ejVa#Zk%t=?#?GZ zEv&V!XM``K{1yHU1TU!(DA>MpSx@`=ayVpa5T7#bE1q>qrk0_tpmadATnU*QJ_3}c z&dF+m<{M}_qV2UG9F0}`+Z*M*9V4eA(+A7QPuc}b3XWQ?`;}1_+6E!!kH{E7l`j3> zuI;bTT0YUKME<|!99Neg;8d-k;=p)JZOrw#b_p|X!+p$bsVp;#?N#5td$A%(h2^l; zJ^AmtemcXguK%?D8Uk+{ZiX_*pEXP>WiUxVjS3zUFwM!mS*^@fy(e=Pb=mY7P|Ci# zRhSdfo_p!z%~xBJa4O#0J=V86IP(q-TSvcfAq`Rp?HEpn^FOOgV8w`=HUDCFiV90~ zsOuvtO9{!(c-nTrTtvfC5)dk5VuYj}$sxaM(7o|9cHnq7ef~CVV1%^qD^m?6)PtlK zoksW#{?T}RaXL7AHi{+UMG2y2og0ZYoWBfYJYw)gOUS5DH`V8D9&$wGb8DZeBHf!d zZnEYx(S*s0Evfr!E{UwHd05t(Bhx8_8#I}A8?+P4!d|PiylrSHIU?EIs^tKyd#Tq* z!U9rz>-u(Ekx-LRsOq)DWyh*y?;Y^#^BZFmW6L`)*}E)>X>AoX_ti?v-=Ri~@A@d7 zahDMX7*)OUXedDQ>RAX{tuFOFJqLn1)-6e2U);0z2cRUVuY&wI4Qan=*dt9bhL-wz zc4wDi@sO8j*spdXjO6Dy$TJRNm>wq)Car_naJbB_v@XiYAv-x9q)Ur7ixTgw;|s$( zx2!{cswMk`hW(^+#N>%$L=N#Ktu2}$MQIpufvap)_T2|!4ki{q_EHeGOBz7ak)mD| zjfUL@Ljbvp(m##-##x|mZm)AginBs$a>kK!8L)C_-6xa$9!6?5Y*Y)4pO26Km^OL; zReTpN$4eD6Z3IT195x1(8P5n(k}>h1|2l87rT~R(LCnRn-7vJ+Kt;d@TKu`8Fb_r1 z7U_Xm;P&cj5@m$jt7rwqtgKp`5hU83mq4N zF!p^S$#0V}wa<|H;4XHGDjBRrhj2DuY)(gkjxfcGE}qLt||K2J1@ov zmA0o-eiImy*_FfNg4b_SY z`5lemqncYsO9Q#}Rv8m^P1`d~=Z-6(hEQ=%*)s8kpvbw(Ppd>KMtjm5%?<1Tr|_MA zA$ffIjaJC&my`=4Ble~BWOY4+Z`wddV`XK|WjNm8t73rhBh5suy9+PEmZ$uZK#_)6 z^%+@f!*No%aY7*CoSySp5P_`Y(ErNbcD($Ji`*S&qD+J5Q(vn!2;KG)G2D1s-+6q` zyvnu~n>#$-_U}=E$v!f($Zpk3!USO>O|oWYhPgC_M2Gs_dy~}>7ZYX~0`;PBJQVH| zP)0oliYkfILaysciu0raeRkL~F|UlBe%VIh|Y>ETq@%aG~ z(z{=r2+Z?|is_!OI5vX)T6qbQ2_hIjEb4NR$>OdF`^e5+gduzle+MXGjl=FH0(6l%(R2d!<#dvVqEE=gAa?L7D zJ-TwTYn$GhIyTLDwzv~C4H0cjDFBa;`Ian_lV7xmU}lMt;T*rKvDDZofEO&Gu38)y zm_YaI#}ocdYWv5&8ZGZVtL&cJ&DzEVKQbr9NS;eO5Wam#$B{~I<~*qqp{LgYs~XPi z#8tyv#=BRDUJ3F&;EBQy}M ze@=#}Ww15P>UcU9ma3qRZC0J<%jA<@0-^&&_U?{)reW>)@kyl+`>vB`k?_FS^W>|Q z$8?&b9CfY_VwHBcVkyLMO1eXHaGwkHX+L1v(4Q+%m-BpX`lsFUTKd_rhIc5ByZeNz# zMjIg%0lu*snUV=CwIB^~AJHmGxW_WWiBQu)XH1qI>2e5IGa@`czQpE0J0Y|Kuz}i# z*BZ`sR>y-m!9eYdKl1q*k4;V8bh-Eo?Y2R+V)i3o{^%)pML~^$h;QkiQWmz#j%MU- zKQL77h$}75cHxlO&7-VtEU3NU)IRc-jw*O}dzGVvl8m9kv)NuDSk#d?*&;Y}BrBV| zKJ}6Ao0eaPF0oS-fkTu(>$+zwUoT%WY#5)MuK(Ko1Q6gdjls)PtXuc0oO=Qb#eF{bNsa?Ubka4JCVi$K z(4hz;NM_1PyEE*5TMYXyK&@t~lN*CqXmco+4Z?Q6Q!`2a=R^Mg2rR2C?h%IOP4vn; z_&JiEpYdsb&IagJMdL*h;83d;?1;%dIqLr|tT8MdJhGtbH%5LO` z>AY(PT(}TbaiD<4;X9$YmqVZ=ejT>EqhI;zzg@ze-_8aPEiL+H&amLPy|Qf}euGHv z@Lf5W_cl$I&;e~p56w{wlmqf^5fR%QZ#x;IQw^cp?q-F+0IYIK|?y#GV-KoprH|g|iUqJPmr%Tug_#X*# zCA{2?#=*+&Kd=h31v(~q9*Q6|i8Z`pY2RyAo;D!6@ztBlo0n5d4-$U0{Sd;T)NgaC zSXSA|)|0rZlmge()3AizGm?@S%M~iiL_=ISm4j*T!)1z(axe0e?rr8)x9j9FFKz&8 zoHzxr)m?y;{GDq2xOd<~gOef>X>a4sbK_wz|JrkU>=eBWhjhS@|3~mUW}?w|^@{oR zjWOm<#BZHyCwPa6fHk0n)@#i4kh2bh+>-M^o}))SOUq*R3Q9#PVv4@;N?Hh#Ge@p-k)z=++4esoR~ zU;uO(G_TZZLm60HgqAbza<0P})Q z6#W=#$PAAttN&TvEmd}QvR?(#IW9s6NNwlSUIBs81Y80^iaeiB^5c|W#a6C#~wCyMO@F zoNVRlL@2h>_MSt9ZMH=m+X7F(JA;SgdD8e z>jbag6J9Bt4rf8aLp+!hs3lh2gc*~4hB7HHl=OivVun8hpb}2!i;+#~F4thMA}OUn zO2aAT_mz^esB{v_COWGMPjsV17=(e-Ra1k1ynW;ISM*5+)V*PPagS8-U1O4sS3Y9Y z&le{<8A01_r>Rr~L7}NmpQ~wLWT4fd&|CPE(&Dn;rS}PpTDXc2l16j~>}N8-;r1h0 z6o+;hBbl)&$5R4C=LWMVjU+$M`exQUnPkCA1`CIKucjD$t(Goip;ApzM!e-m)wDy| zwSV_y4|~HuG}w-^05x)3pFA*O7H_u2u~dAdG78!O4Xr2+koT4~JW%MuIpD-K+8@$c z#pNz>C1b0?C|aL2a%bVykd~cHU2IUqxF}LVQm67A@bmm;1Sd7*5Ngj$!6JYKu&&p% z0ZYJLN5^tRp?5UIR)O8@62$k>fIQNLvNt`D_�bwXV5#fP9gswU>tvJQRPR)RJ`R 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 yNOkV2QNZ>0;Q^%1lvrMv&i_3MsrRO2MUVKq4qU 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�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
i}9r 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

EZhl&B$A*5^XVJFH9~-oRnC`?7P%wye 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

iT#$k|xll#dM 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

~84~pDvL)h@MS@A- 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%0A8NffzW*YTZAvI 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%0A`F-*I$I&M? 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

Mg^XBhF$xyvW@~GyYEUNCz#~>H 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 rEcxTO2*tN>_KSbC``U~z-{WBc}r|A7RX3Zrp diff --git a/resources/lib/deps/Cryptodome/PublicKey/_ed25519.abi3.so b/resources/lib/deps/Cryptodome/PublicKey/_ed25519.abi3.so deleted file mode 100755 index e047bcb977475d2f2c01489cbc3e63117a119bec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 220544 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 z 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 diff --git a/resources/lib/deps/Cryptodome/PublicKey/_ed448.abi3.so b/resources/lib/deps/Cryptodome/PublicKey/_ed448.abi3.so deleted file mode 100755 index da7209a83d59faa31b1a4322ca6ef864f3be0f5c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 250592 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⁡R;9Uc|hU6I#LVa4;-n7(7Pek zW9NcC<=^J=rI-T8^lrzL;g+Ys{IZ=#s|19OTKMfkIEA)!ahwyGk>L7f-BefHL zKuNYQW9pwgBQ^Q!8G7>-jwz#eYd#z%%6weGU-XJn5KeMRItc3>shx0&l58oMI@&W* zQ|#)Vx01HrYbSiGBefB}M@d!{QxADY!tYiwGW8?RsK{L;mM|TR{cKe%t9s&Vm4^Kz zLYjtQg7$pdado6MXY>Hnd!yz%;PST+dM#bl@>$PC_@EH+4Or}MD?jt+gI!9= zy4vTCa1eZNQLNP2&oXuM?z{7-K7lOsaX{@^P0Axl@|0lqt+495>eM$ zVT#6J2q%o&)#~h;F7spjU@x1nTdT-QWXd})YxZG<1T=lTv$K z(;4Y?k1)Y{N`sZkAd!&DpqIiADbALLLg{??l90}a*9fV{dX1{+)93Eqv^80ANxi;^ zBZM2pv0b6@5V9m_Vrg+Bv>b|K7lC1g(2HwJiyNgA(2&Aqi$)V&joyvW3-6-v54qTD zA$-7*x(FY3q!z;eaHKB6KRQwiVUz4A;U^b#J*E)vhnb&oIJ#48n7!cS;?vVBB(nX0 z-*Wli`Cw^6LdFv%vM*Fqy1Yq*bOt6c_YTty3$$SyB8~82ks6p(EYz_s*@E)Ho`9GQ z01b>Vru{Cb{dBMWuHmG1n3P`eO9r~*tKP?i-*%+bvxHpmCpErGD;-yL&9Ol)b^Tro z;XJ2e9V_o5H(HI-=~)G5s=&=&@D_!y@T6BNyxWuRQTP>4`l`YwJn1(IXE`HV8WeiF zTWI%@&hAt_dw107&fB?e2dCDQVZeZj?}$hOZy7FhC%Jq z6(%aq6)l8sb)*pnksxT*O4#axqJ_}=r-T0S0y_wO4P*xX`6|~7>!C28Gc{sja*^xS zoF}^SyPWhx#>&&gac-yPz1f$F(2HxMAzs`e2gg^az_)n8Z&i4oCw)}mH$3T^3V-2A zk15380=HHNEd_-$J!zIgZ+8przQ9#)hU;qrHXsV683>GB1gJ?~#zyDrU#xlll zS7AMce2P1u-cDF{q&C7eO0wM-Q=1**2+u#EP+1xrYniaakva$|*!D*H?E+-3)E{4$OOhr~McY=!bOj(_dJQK%Yd+#2r?Fqd?$ys|XiPsouwcZMY zp<;!J4o|GByhIaM>lK-^$~KbnGL`a*&YO_>!eB5<`CyjvtST|~u-rd!=`GVvnE0G0)>WAJq9;zP zd`lC*&}(+%fmyWX9geX~NEx{S_Fz11l=l&B@~aaXdiKUN`#+sr1JL_Qc0FC zZIsgPEFJU&qBLoBmOvXdqy2t-o4tF)c6)_437>MLHp03cmP`EBUSVpvQgN7t@cb%9 zratW%35$bqiA>$3R7fQ38j3SA^%?H%nmo&P=O=`tZbj`?xg&0TcprOu&c!ZACoMr2HTvn%bluC#}`=!+v= z_+|}`sgk9OQ(|#`;YwH{{H-H(r%TwKE@3HM!tQhlyVE7?PM5GdUBc8P9j!g%=XGi| z>qlB$(tM%O{x9}hAwna5N@-}sgr69&Y}kY%WIRjGuWOh;>cYK+@K=u1O~`7KoWzZ; zD!U08t-_X3(!(Mx)&OL?N0Ek<^Z^%}Erh>uvDra5Lw$iwlFwhmqO)2Jk%>jFPiP^$ z(2+U_cRErFAtM4?qJ(>$(^?2$>qs4hZ*-&Ff4|)>ee>hSLA?pOm8EwIUD~E~X`9y3CK`Y~fr@yo?px^));&5wLe~6$Ul`CtUd2>nu0GYJ zvEEZ%^wjaLzv!B;@Jvr4yhuq!Y@aWheRa@SA*I#d8Mb#wVO;n;>8j{TS4Aoe>GyOY zo_z??QM`_<Gwxw%Y8mti5COZ5Ws_(%Ix-vgJE(pi!q_?JX)69xN& zGp1MA<4_j@m-TX_>$1BE*#$-DmykJaK&rnn`}~NzWHd={o(w0nvSb{E0p37QhEPUp z41$x?f4(3Yj~%RgZqh!O12;M$4l*36K}rseASK5^kVbNNTF1Jyj&*4r>(V+>JJYPx zw2rQ{j;^$huC$I+Gef<(Gav`lU-eho(n{SC=lta7=4+T3qr@@6EjM3NI6hZNnuUqR z&DSIiGsL86hB%Uwg-+w%tU4pSBffZcN~4mKI+dK%s$@1S&TEvMoP8=eX{Dr3RF~bU zS3HqUngCKVCo0%%Mlko8nmY^zhAY==;#R#*32wRenu7dmnm!OLJo%(ta8!MTcX>T3 ziwoayNLpi5B<%?T+W+^i9I439VoHT>7E@O2q5Mge=3&K0RR1H+j`E`lpY)`p{n(U9 ziIX6vG@rFSnH7)aQdy0U(s@(Why#P6dDN4V5LZt7#{~UB!vMxo5+2tA$yX2hgBca3 ze-oo$2|Clh6sgGyN|pPZX8XF!j%VMPQ8DeauSq(=q;p@?j9+#Ft~3Ie{bflzN0#Ssn=O3*zwo$CpQ#N%#{+8@pqbGPIU&IKNVs!Oty4!O3C6JhgD5H z-!)aKc_X@8T#XK9G-v-*jYUlo(jZJb2)(OQwuuW5)2(JOJ3%||SLzkDO!x*RMF9#p ztHLh%lWua-!tQv5E+CXMy9%Y~t5xL7oVIR4YHh*HwrMs#0Mj&k$#h&6Rws7`GiS_9 z&=N_*V)+Jx88a)C{90AIP0CaI)J!Rr(PF^DETY^IeIwjU2{HT&XNYLYX4F$H-HCY~ z{l-lckkUSztD%6;Ihvo0DNhijnNs~|7E?!BooAcp6t)n08&dOJb9w?D2`yQuxg`*Z zFZU7&uW+PJLa!vN7lp;=Xr4>;IxE;0Z}ofQ3DcIzxLz~Nsm>r>z^&dvSC|qZW#F>|guZ-P;-rA7pO)(_ z=?!|tQwqYPZm_p-T7~c!CE0CwrdBw{cEXDtX&qr}RbHkp_l$&l9jTr0WsbCt@Ksgf zn7Z9DwiCYJkus)zDSrfX$m^X$%7l}hq&7mjKpax8Fm;+^EE8VnNNt34fz8X*b)J!s zg|m!Iv3wZ%2z^o8(gn+=u()q_#Vrv|ah7xuvVdYq$rYECj&YfAwa-h)0@}PxvC{ES zg>b;-EfL=6NL_?VuRP**cpjQ0BTSGYB3GJXYy^hpENM~Xfn+D$Dxd>_K2z%Yuo`$XVx8Ll*QNw;lb)xJely~C+&A$*r3wG#4*X%68(E0CnXXr4-Ctp1wkHl(KCX!en!NX{3}TZDne@ ziS2``A3Bo}En14@UWvoe1 z3I!n0(o90(UvC}ni$QpVl5C&G)J)HKxDv}*63dme#MI)#YDn8y%92>hkT~Kbkur;} zIIV<~EicCCU79McXw6X5I=U+Yr=*iGF0nI1VrRBQQm<84-Kkdu2I2d41&FuV3BRo* zTa!%v*fSD7Ud71N?;T@1VU4aJp@b%-uQc*eXJna>HehB?NEPpdm(fR4Wmmr^&>?0L#p!YTJl=# z)acV^lBjfiaLdQ=Eo8l$bb2f!;~{yauDhL=I|%9RBGfOMJ%o*6QfP(9Uo;ziL1QJU zI%9G~)nC`*v4WpTU8mJ#Rv}e<)0L__opXd~IrFkjU?xULuiNI4mN-I)Q=%_$RtJz4 zr+%W}c%?XYYa{k>X^E*`%1=ZX2sBa#RN__6jh%#LM`|UcchCTmnzKe{svb>olO? zyvMTg3R$MaUgA}*eC>pHDya|*K}}9;GrXz9n-RY+lX#2iesbYmdLbWXH`S8q?2 zMIC92c8y?>p~oy@*I5W`4yI|@ICiB{KoV*0c-NcCUE0bkZN5a^K_k)n3F)x{wkESZ zXy`F}shT;c%wIRFI&xxK;%{1HmkG4%a#vkuWNMmIA%$jQc0fp}_#nRwV_;4S&ui#h znJBjOD3ro-M=14ab5emLVQ|_U90Fk6P%CciG&hn;gMjMDnhQ20nQDTxeiOM#Mp+4` zSxtb@a<67nGItRCtrq=Fdd&sv(@eMOO)qI1uWa>6afKo|cx7u$a!z#BLhnVk-5#1T(?Uw&H5ZIh2dw&x+AIW= zK&Fw~CrxeGXVJ&?ij@%IH?Fd`FeM}WHzmnLroF;c{%SI|6Ta^nk}{u_$xUkB?=+VQ zU*kw^w<>&tCsFNhoqegU#UBAmYbjC7jVf{+jwv)?x4IF}VJaljgs}ns5?L@qZdcN_ zl+w10xMYr(`3zT@R>E(&(sU70xLt)X^_XWQq$eyRQ@##TlL_>Wu^;;A3u=}0k;LD3 zmbMUn$r;c?n^^6VllTr70zHIpb)*(TZ)8u*A_K!D0co20qnN40$2hxM2)(K6MlBBRm>VXo$D{wfsc?n;@|Z;*wa|KR zKpPFX(;3i4_)$k{A$-8uv~y2|3EBoH6M84NCC$t@mtre2ZQOH=i?qP!FD@h+&QfRi z!nUUitJ6)!lR44l5-(B-tMrP~+H%yJ$yX>j=Gbp}5+T)VW}2{4n=3N21^c#?dW4Y= zSnKaq4OyLE5WG?EvZ6E2#0I$r4f;*4J*2mgGMG~RMV6#g3zeZg=cLVb?F}EyV4};} zQSMHemYPdp7*2^3XHc3iRjO{ql-a6bgHzf{$edx&pUY~ga=y~#r@FXmT0cQfpzhbb z&c+JK)H5D$4A`AcIgLsxwI?#e=HyRqx!S%;uLZ0O<(MMV6ooC8WL}fbovRtocLMAQ zRHiRcI=V4J$}9wXM?QgSr2kL@<&S#B6rM1zwTUSU;Y20bDLhkCJR{*%Rg9VCQexVG zG7Siw+%`h06a&f?rq+5!!rqtxlNJr;c3^~TbMi&o~0xSp7sh;7kNg)OR5;Xx+J3d4>35v+7Q*>X zW(VP!j?_*&s0J;^7s;ZWTo9E%SK0KSG!f#Wlh{T$MM>rm5EI9EzN8wPZqTP=tM!T) zA{^^F)HcF-O0qFDy2Oi}#4=%nlh{ERmpD>w)5eufVu_Gq@ECN)r`!(BK^Uw`%_r@Q( zjgebHRHxGn-L9xFzM5G*|Y*Ul-*R`7ITV zN2-KRDoNgGl7~D@|5@qsdh?#35f6DtVXVq*7k4U5(_Z_StaIc?PJt^TjlfA1Lh6_K z?MU|Qt+I#VFZSS0h3RU0){JaV+By=+TqIECC=b7= zj;t9EJ8FWoeiM($BFMw9G^+^}n(EbLJp6iGi~d`^W;`wlf?w!OFDc`}3nh4_^+{uM zuCD2VjJggFhiUA17Mj<{w%4S}G-HOh^EH&hD_eh3T%kw~UfC{_oD*HO(0h?>!G~td zw2)GG&3yR9I$+gj)MmleCYeTVnby?Db>9CMy<(UnoaCaWgRtI_+6kv9DSO_N3-Syv zy^i)3{QGsn_Y=Ls7{V)^eQktSDamSP>Q|mISs6@IP3Tz5ggYJSGC~Trvva1rn#(9Q zd8L|fxzdyf`<2{3 zlxePGzB0~iHq{(6nOLm3C|1+#tBQrj+ww6Do*36e=dsAoD09*s&nR=!LGkogFSe<( zUu>`FB?zx`q;A5WUShdyFTvE4N=076_i4MZj7)vPHQv;WIBt_*+I6loqD(kV)#1s| zllz&VEw*D}isspbk327u2tc0{sWVdG6+nQx>Ys%@6 zNNee2E0HOd0gP#rt10OMbg}@yb!F-yj2ECYqX3;*1?bEuKu=ZyIxFb{^i-ScQv+(< zYn+uWgtQXJW?0n}3=*MtlE$)^E$CvE>xH#Zn9r#@13qWY<-1(-Be?D!_6!!uR#Gnb zi?T30Vv*9}+w9wiKck6n#)(Ohc-0!DFgRL;HtQ7|SxG%IZc=AV(E+FEi>e5R`wrl6 z9}eIpwaI`d|L|uVl$U};IPpjI9_HeNpLAuw6DJ127T3?UUayd~lrfDLy)2Tiom50+ zexW^S?HC*zE9}E^)08Fl%WU?g3Wv(PbC{*`y1Q5qZ}kWR{dO(>d-ZxOjy4@($N5{{ z4p$#F#U|$2Pw`Oh+3J-;u+w?3K0NkVajPJW+@nSwgNMKzuKR(i_iKPSQEgR!-7ab_VW!b%(~rcz3*BzU44ZHQPZ5F~ z#qpZ4pJ*iOO!J@hirFIJ=aeL)etW9CLC~!C?{ryHD;8_}dCIWPF}4zJQBncNFXdBF zyEN@(>YBRR3+E~uvttsL&Z%pMc+PiSWK&<)EA}blw~0Tkx4-4sIv-W|geQ?ZeqErq z&va}h!VQj;(uuIg=hWCyP{!$+AqvX3*4#RUNnWQ5S>HNon(>E|hk`$xe9Rw$3V7?t z!|Sml^0tC~JlU40#^Xj~Um_ZcWM%12K2(+~(MTKdJ-1tn%(7Os9WrabYy+FD%+jq{ z8xN(m<-ep7urQpaOt>Crw){6OOMfr^c*v72bBELfSdv5tX!YB^vjb)3EM?D(-%#A=Pt5^2Z-)YUH>5v7Lqa zO(kckM41{BU*c#@6=GiHlr8b4j&D(gc)O#WRw2H|(N3=rzuwUnSBT%}XlGQ2dCdn) z9F5)(n~A@zd9jEhWVM9Bu3=Y4-h&-U4~n={`-^G_s+#n}kx>)e z@@8)oC0J@Qze#K0ww!P8zE!Vi5`;f+q&0+RUtqbzVDFv^6PG9vqu5VYsxIfe_CR5z z*U?%Njp=fl%Y^G4X)R$~V)j#e${Tc-&4R_iov{m6iP?SDB^975>JPSig0y=1Q{^&haEx z`9FHn7ZiTQlUU`Cd(u;cuA0^`3V20lPsnJ%RF&{zm$99JG3ZHGDZI&%%8Zq_d(yiV ze$bN`K40~uA1M5>Co!U?xxgsTQ^?4|mOo*u<0`RB>GLGU-77rlZx!C*NespJdlGvQ z#vJ-J!fz?pL@YJq9S`aM>ZN3LG>nvFJWBT;Vw(psme*OnO@UHVD2y3{`&p4U&^x$y z!=~OrM&3_cn6)$VrZFCZc81wUJc%*&4NoF;ffTGI>~SRWId4P~I3ZfHl z`XsYQK_RfyC<-~!1znllNV8 zZbLPuMNLjbnUD%aeYwKapl2k!){#01z2Vq=&)-vu&mBPvzVS{C^ve6`HAf!jc6ImLV zm3-K2sIGULA{(n5V-z8C+S<>`j8BEDflHiMMhoY-#Poihp*AJzs<%785yaf zg81*8+NKII1!iseRGV?H=AORH($TjNesDQS9fY4&l2mVdg{kj2#&*I-9jSvbTtRW| zgheG;arSolH%5Eq-+~*O#Rs@FB8T!jn=c^FD|Q=zc_6rLV5vlKu8ye0VU@U z8UjYb-#ME~gmIfj>u$VF|K)6IC8Qzf3<%>kwPx7VMw=dYHnkGQZOZB)ye(t2?$D+P z52sE_JZR z7K!568n8Sf^y0eG;<~at5-%J3o9wQK0@<^Gi_rTn-EEEuC*!{{!5G=%l6m;a+QBVy z9bM}y74ixP{ZvW3BErtrsCc1^j@C~r{JAF$Yf#+ef}{Lug>Q4DmU|U?hqa_bDD`X? z!7p1m5lSH|nVOmrk=bMo?Y>WqBn%-4Snq)^JYmt1x(Mki8Aj!O9;TN$*79WvH|n(( zYY(u>ZX`)B_1xDeyjHITtX1SqP^M!QKzN(a{T_uM(rZi?NuTuG4=MbPUgdomF^lO~ z%@Y3FG@G8C9^9DHatZ4={S^*)08f$w(2DMB! zh*rN6L0KByVodeu;6vX@ z*sftDMI>gl(H%_R>O+oE$SaCS_;gGn@hlfu`;F6>W-KB*;SOaLgNPY)h$6zQC}Olo zR*e#Okqz-++&KT+Y=FxNP|)#@!@*ML^p=$hFYzQoR;M|O$ixB9_ZEfk@+3l5p~=d` zW1jCRg*DFEErc9-Ojaf?^n7a+_IMH@M=Fz*!_}`nUtYal)GItq$f=LXx<>DB_I%F8 z3NT8IA8%g0%VVRLe%WPh!ohQr7G#ja_r;*BggcJ3jt_lWa0v8LN z!BoH{O_Fr7^QKAc!M#aZ1$CoTK~h*F=C6k`ii(QbBg{sgWGgtFqHsogrt4W!U6YiT zy)g$NmX~SviLMGpE-yu$ry36@;KYRFPjV2XNgN5iNm>QdMyZ0Nu){HmiY|9m@N7Dw zAb7Rr=e#PT?~t-5M{bsv!}kIm2-xxN8>!9|&ph(}CI!rM0!oB0R_)mG zCv1xuRH`sVO(Q%T$SPthi}*FCw~dhEq}1sR$9!Vc>r7^6 z>7R=uWN9Hk=d3W7phvH=Kq3CP(!yXL{+^g79?&b!t`L4X&KD$pWMfMnLC8;9S(8)N zyTY&_+}0A_=1672JCsz!Tn+En;B0x)2;~2kGOcw1lbYIuMw#8R^>$IQIN?<)gc{NA zKB6SLLcYkd;AB~8lS0iRfcTG^3+-r*8g!;!mJrk1oWZFnZ!Q>8IoCRQsc|pw zn7q}gjvyD{C-;O(`JIUnzlg8A^|FuXzfT<47z!i7qzm4iS}9?zS^*Q$&^ zxgxX^cD%?kV?#pHr6kM96iqZTnTXpv!oxQAsuAtYJKwev&Ud5^!ngS%d}Npj8ZDN!x+472 z6`_@o_F=dojGH>rXcYu$BRgp%mBRsd4m0tj)7?o(MTW1s%F?N{zBSZ0(>Y}wA*&J* zPe?w>I{kQIwax!1M4 z&nVvn`Qk=lAb=HkUARIrD<1EY;_H1^N&(PbG;-!@QywRhc znSeP|(`=`nWO^%=pEu~Mm>W+^xRHUEC?UygX9){PW{4u<30W_R9M$z8o8^yd>1Svm zVZ0xmexM|B47Z=Hq1k)ixwiU$1 zUBgUJwH+&x+N(zp8ejyBWCF{?kr=BnLh8bJnN$Q)x|luLI;g>m>rRX7PK)c#7H6px z%D(0qcRRGk^v_9q3^8rdx=f4A)Hc+RU_D9KNqo5rmkz>hO2SKT`zuVm-18-MXZ6In zFc&fluUIWIE3X;n>vBP|z?pqnnkf~?K}wPh!K9;|PW;n$RezlJefVd6)gkI?%gwd|93 zW$-Ca&BLqc&1uh7&}$KE7rSC5Wt`r@7p{lx-$AtZlNFN-KCJTp+1ZvdL?$z2`>bOt z(F*FyK56*x+Crwec}!~6E_-o5RkkPexpXUc`?~` zo>`%Ur&ZFKUeZ8Nh;6VVRlT{LmX&muJQzz_y2#5vY)ex{p6-P_10&O8o*qS@jl5z{ zkdW??NA0rEW;)dR8H4GA_@zQZjk_^qZzP?PFBD>Kp&pEigj6vHDr5f7s!G(PvxpUV zqF5BmmsxL;F=?2zpG(yGTV43I{ZQKm=Or1vZB|aZph{-8)Wg%fCYc!{;Nj)$O6N=+ zZi_@z80I2Z<}NL>bQ#32a(-wbe65l~^c58*yuVtKy0Zp0Q;b}nO4_g2S=b1RRoqZK zA&sp<#+x+5oazia?Y2$4@=dn1k*V#l8nGy!&()0Tjoqpj!AeNkm{bvZUz85d7Xun$ zU)5{o7uj~Cm@)>tB-04t79{y_Hr7;0)vq*D_IMZ2eqN=EI>f}kxfn11d&;`hW9|_3#hkoKWfA*=rDMls z^h~L-9z%spryMKg#&yulF__jgU?E!};b&^Ud|_DLvS6C}gf3q#s)~A>$bC}tKImlF zza_*pW#BEjjO?)ZQ=0L!PJr30t}y)-r6YU@DYJmb0n*(i1&3~3+vTQJsZPK%J%hz> zX!f82C%@6jUqg7IlK8=CrY}-@(SBYUpI#?rO(I9SIQEa!vDnmv1wSE`6oRYG?1O2l zC|Y_j9tlal#tJDk%YKX>Z)_&* z!uct67g;k7mB`H38$MPuL?s!d*)^9#8pDcoSCe3PshNgNoo07SMV1*X_MS9Knayh=3 zrZ>H$jcG}g;F;DZnR2eK>4J>9##ic8-6%|B$FtD9j_{TDG-HOh^EH&hD;s;V1cf3w zcpa&|O?1^l??txFCPk(hGcBYPUNgT#&pKe$XVhlF)h3yIQ%{g#YA7?SvF;1B@xJCN)p9YN+JnPGX6WGHfU^j?_W;f(?QBG38?793{dG!t))egRpJ0&1#FlRJT%*mvEyabr7EX(s*8` zPE#uK5}xfy9fT_#shzORkva&sIZ``ezaw=JzS@!63Ga2J4#Ec=sh#jEj?_W;s3Wx# z{?d^;2q#<-FF8{uDitLse1Ri%5U#Gu%T%vpY$trFBXtno=t%8^uW+Oe!nZk6JKcUgk6r*McD3IO*i4|9jSwmO~)!_ z>dUSnbraTVd$5d5{jE|_48q?yQWqgx3%Cf`TCg-H{GMw|-Gpo^Rw+|AY_aZ#I6^iR z%gEF%E@rz48T6KsDTWCo5;8U{BU8MBF�fXZY64zs7Gy7Gnp8aS7vL)at_MS1ycN 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 diff --git a/resources/lib/deps/Cryptodome/PublicKey/_openssh.py b/resources/lib/deps/Cryptodome/PublicKey/_openssh.py deleted file mode 100644 index 53b16df..0000000 --- a/resources/lib/deps/Cryptodome/PublicKey/_openssh.py +++ /dev/null @@ -1,135 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2019, Helder Eijs -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import struct - -from Cryptodome.Cipher import AES -from Cryptodome.Hash import SHA512 -from Cryptodome.Protocol.KDF import _bcrypt_hash -from Cryptodome.Util.strxor import strxor -from Cryptodome.Util.py3compat import tostr, bchr, bord - - -def read_int4(data): - if len(data) < 4: - raise ValueError("Insufficient data") - value = struct.unpack(">I", data[:4])[0] - return value, data[4:] - - -def read_bytes(data): - size, data = read_int4(data) - if len(data) < size: - raise ValueError("Insufficient data (V)") - return data[:size], data[size:] - - -def read_string(data): - s, d = read_bytes(data) - return tostr(s), d - - -def check_padding(pad): - for v, x in enumerate(pad): - if bord(x) != ((v + 1) & 0xFF): - raise ValueError("Incorrect padding") - - -def import_openssh_private_generic(data, password): - # https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?annotate=HEAD - # https://github.com/openssh/openssh-portable/blob/master/sshkey.c - # https://coolaj86.com/articles/the-openssh-private-key-format/ - # https://coolaj86.com/articles/the-ssh-public-key-format/ - - if not data.startswith(b'openssh-key-v1\x00'): - raise ValueError("Incorrect magic value") - data = data[15:] - - ciphername, data = read_string(data) - kdfname, data = read_string(data) - kdfoptions, data = read_bytes(data) - number_of_keys, data = read_int4(data) - - if number_of_keys != 1: - raise ValueError("We only handle 1 key at a time") - - _, data = read_string(data) # Public key - encrypted, data = read_bytes(data) - if data: - raise ValueError("Too much data") - - if len(encrypted) % 8 != 0: - raise ValueError("Incorrect payload length") - - # Decrypt if necessary - if ciphername == 'none': - decrypted = encrypted - else: - if (ciphername, kdfname) != ('aes256-ctr', 'bcrypt'): - raise ValueError("Unsupported encryption scheme %s/%s" % (ciphername, kdfname)) - - salt, kdfoptions = read_bytes(kdfoptions) - iterations, kdfoptions = read_int4(kdfoptions) - - if len(salt) != 16: - raise ValueError("Incorrect salt length") - if kdfoptions: - raise ValueError("Too much data in kdfoptions") - - pwd_sha512 = SHA512.new(password).digest() - # We need 32+16 = 48 bytes, therefore 2 bcrypt outputs are sufficient - stripes = [] - constant = b"OxychromaticBlowfishSwatDynamite" - for count in range(1, 3): - salt_sha512 = SHA512.new(salt + struct.pack(">I", count)).digest() - out_le = _bcrypt_hash(pwd_sha512, 6, salt_sha512, constant, False) - out = struct.pack("IIIIIIII", out_le)) - acc = bytearray(out) - for _ in range(1, iterations): - out_le = _bcrypt_hash(pwd_sha512, 6, SHA512.new(out).digest(), constant, False) - out = struct.pack("IIIIIIII", out_le)) - strxor(acc, out, output=acc) - stripes.append(acc[:24]) - - result = b"".join([bchr(a)+bchr(b) for (a, b) in zip(*stripes)]) - - cipher = AES.new(result[:32], - AES.MODE_CTR, - nonce=b"", - initial_value=result[32:32+16]) - decrypted = cipher.decrypt(encrypted) - - checkint1, decrypted = read_int4(decrypted) - checkint2, decrypted = read_int4(decrypted) - if checkint1 != checkint2: - raise ValueError("Incorrect checksum") - ssh_name, decrypted = read_string(decrypted) - - return ssh_name, decrypted diff --git a/resources/lib/deps/Cryptodome/PublicKey/_openssh.pyi b/resources/lib/deps/Cryptodome/PublicKey/_openssh.pyi deleted file mode 100644 index 15f3677..0000000 --- a/resources/lib/deps/Cryptodome/PublicKey/_openssh.pyi +++ /dev/null @@ -1,7 +0,0 @@ -from typing import Tuple - -def read_int4(data: bytes) -> Tuple[int, bytes]: ... -def read_bytes(data: bytes) -> Tuple[bytes, bytes]: ... -def read_string(data: bytes) -> Tuple[str, bytes]: ... -def check_padding(pad: bytes) -> None: ... -def import_openssh_private_generic(data: bytes, password: bytes) -> Tuple[str, bytes]: ... diff --git a/resources/lib/deps/Cryptodome/PublicKey/_x25519.abi3.so b/resources/lib/deps/Cryptodome/PublicKey/_x25519.abi3.so deleted file mode 100755 index dbb00d5916bb54ea6e6e54f46069579fb48bd4b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79472 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 diff --git a/resources/lib/deps/Cryptodome/Random/__init__.py b/resources/lib/deps/Cryptodome/Random/__init__.py deleted file mode 100644 index fd18d86..0000000 --- a/resources/lib/deps/Cryptodome/Random/__init__.py +++ /dev/null @@ -1,57 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Random/__init__.py : PyCryptodome random number generation -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -__all__ = ['new', 'get_random_bytes'] - -from os import urandom - -class _UrandomRNG(object): - - def read(self, n): - """Return a random byte string of the desired size.""" - return urandom(n) - - def flush(self): - """Method provided for backward compatibility only.""" - pass - - def reinit(self): - """Method provided for backward compatibility only.""" - pass - - def close(self): - """Method provided for backward compatibility only.""" - pass - - -def new(*args, **kwargs): - """Return a file-like object that outputs cryptographically random bytes.""" - return _UrandomRNG() - - -def atfork(): - pass - - -#: Function that returns a random byte string of the desired size. -get_random_bytes = urandom - diff --git a/resources/lib/deps/Cryptodome/Random/__init__.pyi b/resources/lib/deps/Cryptodome/Random/__init__.pyi deleted file mode 100644 index ddc5b9b..0000000 --- a/resources/lib/deps/Cryptodome/Random/__init__.pyi +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Any - -__all__ = ['new', 'get_random_bytes'] - -from os import urandom - -class _UrandomRNG(object): - - def read(self, n: int) -> bytes:... - def flush(self) -> None: ... - def reinit(self) -> None: ... - def close(self) -> None: ... - -def new(*args: Any, **kwargs: Any) -> _UrandomRNG: ... - -def atfork() -> None: ... - -get_random_bytes = urandom - diff --git a/resources/lib/deps/Cryptodome/Random/random.py b/resources/lib/deps/Cryptodome/Random/random.py deleted file mode 100644 index da30795..0000000 --- a/resources/lib/deps/Cryptodome/Random/random.py +++ /dev/null @@ -1,138 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Random/random.py : Strong alternative for the standard 'random' module -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -__all__ = ['StrongRandom', 'getrandbits', 'randrange', 'randint', 'choice', 'shuffle', 'sample'] - -from Cryptodome import Random - -from Cryptodome.Util.py3compat import is_native_int - -class StrongRandom(object): - def __init__(self, rng=None, randfunc=None): - if randfunc is None and rng is None: - self._randfunc = None - elif randfunc is not None and rng is None: - self._randfunc = randfunc - elif randfunc is None and rng is not None: - self._randfunc = rng.read - else: - raise ValueError("Cannot specify both 'rng' and 'randfunc'") - - def getrandbits(self, k): - """Return an integer with k random bits.""" - - if self._randfunc is None: - self._randfunc = Random.new().read - mask = (1 << k) - 1 - return mask & bytes_to_long(self._randfunc(ceil_div(k, 8))) - - def randrange(self, *args): - """randrange([start,] stop[, step]): - Return a randomly-selected element from range(start, stop, step).""" - if len(args) == 3: - (start, stop, step) = args - elif len(args) == 2: - (start, stop) = args - step = 1 - elif len(args) == 1: - (stop,) = args - start = 0 - step = 1 - else: - raise TypeError("randrange expected at most 3 arguments, got %d" % (len(args),)) - if (not is_native_int(start) or not is_native_int(stop) or not - is_native_int(step)): - raise TypeError("randrange requires integer arguments") - if step == 0: - raise ValueError("randrange step argument must not be zero") - - num_choices = ceil_div(stop - start, step) - if num_choices < 0: - num_choices = 0 - if num_choices < 1: - raise ValueError("empty range for randrange(%r, %r, %r)" % (start, stop, step)) - - # Pick a random number in the range of possible numbers - r = num_choices - while r >= num_choices: - r = self.getrandbits(size(num_choices)) - - return start + (step * r) - - def randint(self, a, b): - """Return a random integer N such that a <= N <= b.""" - if not is_native_int(a) or not is_native_int(b): - raise TypeError("randint requires integer arguments") - N = self.randrange(a, b+1) - assert a <= N <= b - return N - - def choice(self, seq): - """Return a random element from a (non-empty) sequence. - - If the seqence is empty, raises IndexError. - """ - if len(seq) == 0: - raise IndexError("empty sequence") - return seq[self.randrange(len(seq))] - - def shuffle(self, x): - """Shuffle the sequence in place.""" - # Fisher-Yates shuffle. O(n) - # See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle - # Working backwards from the end of the array, we choose a random item - # from the remaining items until all items have been chosen. - for i in range(len(x)-1, 0, -1): # iterate from len(x)-1 downto 1 - j = self.randrange(0, i+1) # choose random j such that 0 <= j <= i - x[i], x[j] = x[j], x[i] # exchange x[i] and x[j] - - def sample(self, population, k): - """Return a k-length list of unique elements chosen from the population sequence.""" - - num_choices = len(population) - if k > num_choices: - raise ValueError("sample larger than population") - - retval = [] - selected = {} # we emulate a set using a dict here - for i in range(k): - r = None - while r is None or r in selected: - r = self.randrange(num_choices) - retval.append(population[r]) - selected[r] = 1 - return retval - -_r = StrongRandom() -getrandbits = _r.getrandbits -randrange = _r.randrange -randint = _r.randint -choice = _r.choice -shuffle = _r.shuffle -sample = _r.sample - -# These are at the bottom to avoid problems with recursive imports -from Cryptodome.Util.number import ceil_div, bytes_to_long, long_to_bytes, size - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/Random/random.pyi b/resources/lib/deps/Cryptodome/Random/random.pyi deleted file mode 100644 index 9b7cf7e..0000000 --- a/resources/lib/deps/Cryptodome/Random/random.pyi +++ /dev/null @@ -1,22 +0,0 @@ -from typing import Callable, Tuple, Union, Sequence, Any, Optional, TypeVar - -__all__ = ['StrongRandom', 'getrandbits', 'randrange', 'randint', 'choice', 'shuffle', 'sample'] - -T = TypeVar('T') - -class StrongRandom(object): - def __init__(self, rng: Optional[Any]=None, randfunc: Optional[Callable]=None) -> None: ... # TODO What is rng? - def getrandbits(self, k: int) -> int: ... - def randrange(self, start: int, stop: int = ..., step: int = ...) -> int: ... - def randint(self, a: int, b: int) -> int: ... - def choice(self, seq: Sequence[T]) -> T: ... - def shuffle(self, x: Sequence) -> None: ... - def sample(self, population: Sequence, k: int) -> list: ... - -_r = StrongRandom() -getrandbits = _r.getrandbits -randrange = _r.randrange -randint = _r.randint -choice = _r.choice -shuffle = _r.shuffle -sample = _r.sample diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/__init__.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/__init__.py deleted file mode 100644 index 330fa22..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/__init__.py +++ /dev/null @@ -1,60 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Cipher/__init__.py: Self-test for cipher modules -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test for cipher modules""" - -__revision__ = "$Id$" - -def get_tests(config={}): - tests = [] - from Cryptodome.SelfTest.Cipher import test_AES; tests += test_AES.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_ARC2; tests += test_ARC2.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_ARC4; tests += test_ARC4.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_Blowfish; tests += test_Blowfish.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_CAST; tests += test_CAST.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_DES3; tests += test_DES3.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_DES; tests += test_DES.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_Salsa20; tests += test_Salsa20.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_ChaCha20; tests += test_ChaCha20.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_ChaCha20_Poly1305; tests += test_ChaCha20_Poly1305.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_pkcs1_15; tests += test_pkcs1_15.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_pkcs1_oaep; tests += test_pkcs1_oaep.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_OCB; tests += test_OCB.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_CBC; tests += test_CBC.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_CFB; tests += test_CFB.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_OpenPGP; tests += test_OpenPGP.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_OFB; tests += test_OFB.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_CTR; tests += test_CTR.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_CCM; tests += test_CCM.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_EAX; tests += test_EAX.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_GCM; tests += test_GCM.get_tests(config=config) - from Cryptodome.SelfTest.Cipher import test_SIV; tests += test_SIV.get_tests(config=config) - return tests - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/common.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/common.py deleted file mode 100644 index a13d4fb..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/common.py +++ /dev/null @@ -1,510 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Hash/common.py: Common code for Cryptodome.SelfTest.Hash -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-testing for PyCryptodome hash modules""" - -import unittest -from binascii import a2b_hex, b2a_hex, hexlify - -from Cryptodome.Util.py3compat import b -from Cryptodome.Util.strxor import strxor_c - -class _NoDefault: pass # sentinel object -def _extract(d, k, default=_NoDefault): - """Get an item from a dictionary, and remove it from the dictionary.""" - try: - retval = d[k] - except KeyError: - if default is _NoDefault: - raise - return default - del d[k] - return retval - -# Generic cipher test case -class CipherSelfTest(unittest.TestCase): - - def __init__(self, module, params): - unittest.TestCase.__init__(self) - self.module = module - - # Extract the parameters - params = params.copy() - self.description = _extract(params, 'description') - self.key = b(_extract(params, 'key')) - self.plaintext = b(_extract(params, 'plaintext')) - self.ciphertext = b(_extract(params, 'ciphertext')) - self.module_name = _extract(params, 'module_name', None) - self.assoc_data = _extract(params, 'assoc_data', None) - self.mac = _extract(params, 'mac', None) - if self.assoc_data: - self.mac = b(self.mac) - - mode = _extract(params, 'mode', None) - self.mode_name = str(mode) - - if mode is not None: - # Block cipher - self.mode = getattr(self.module, "MODE_" + mode) - - self.iv = _extract(params, 'iv', None) - if self.iv is None: - self.iv = _extract(params, 'nonce', None) - if self.iv is not None: - self.iv = b(self.iv) - - else: - # Stream cipher - self.mode = None - self.iv = _extract(params, 'iv', None) - if self.iv is not None: - self.iv = b(self.iv) - - self.extra_params = params - - def shortDescription(self): - return self.description - - def _new(self): - params = self.extra_params.copy() - key = a2b_hex(self.key) - - old_style = [] - if self.mode is not None: - old_style = [ self.mode ] - if self.iv is not None: - old_style += [ a2b_hex(self.iv) ] - - return self.module.new(key, *old_style, **params) - - def isMode(self, name): - if not hasattr(self.module, "MODE_"+name): - return False - return self.mode == getattr(self.module, "MODE_"+name) - - def runTest(self): - plaintext = a2b_hex(self.plaintext) - ciphertext = a2b_hex(self.ciphertext) - assoc_data = [] - if self.assoc_data: - assoc_data = [ a2b_hex(b(x)) for x in self.assoc_data] - - ct = None - pt = None - - # - # Repeat the same encryption or decryption twice and verify - # that the result is always the same - # - for i in range(2): - cipher = self._new() - decipher = self._new() - - # Only AEAD modes - for comp in assoc_data: - cipher.update(comp) - decipher.update(comp) - - ctX = b2a_hex(cipher.encrypt(plaintext)) - ptX = b2a_hex(decipher.decrypt(ciphertext)) - - if ct: - self.assertEqual(ct, ctX) - self.assertEqual(pt, ptX) - ct, pt = ctX, ptX - - self.assertEqual(self.ciphertext, ct) # encrypt - self.assertEqual(self.plaintext, pt) # decrypt - - if self.mac: - mac = b2a_hex(cipher.digest()) - self.assertEqual(self.mac, mac) - decipher.verify(a2b_hex(self.mac)) - -class CipherStreamingSelfTest(CipherSelfTest): - - def shortDescription(self): - desc = self.module_name - if self.mode is not None: - desc += " in %s mode" % (self.mode_name,) - return "%s should behave like a stream cipher" % (desc,) - - def runTest(self): - plaintext = a2b_hex(self.plaintext) - ciphertext = a2b_hex(self.ciphertext) - - # The cipher should work like a stream cipher - - # Test counter mode encryption, 3 bytes at a time - ct3 = [] - cipher = self._new() - for i in range(0, len(plaintext), 3): - ct3.append(cipher.encrypt(plaintext[i:i+3])) - ct3 = b2a_hex(b("").join(ct3)) - self.assertEqual(self.ciphertext, ct3) # encryption (3 bytes at a time) - - # Test counter mode decryption, 3 bytes at a time - pt3 = [] - cipher = self._new() - for i in range(0, len(ciphertext), 3): - pt3.append(cipher.encrypt(ciphertext[i:i+3])) - # PY3K: This is meant to be text, do not change to bytes (data) - pt3 = b2a_hex(b("").join(pt3)) - self.assertEqual(self.plaintext, pt3) # decryption (3 bytes at a time) - - -class RoundtripTest(unittest.TestCase): - def __init__(self, module, params): - from Cryptodome import Random - unittest.TestCase.__init__(self) - self.module = module - self.iv = Random.get_random_bytes(module.block_size) - self.key = b(params['key']) - self.plaintext = 100 * b(params['plaintext']) - self.module_name = params.get('module_name', None) - - def shortDescription(self): - return """%s .decrypt() output of .encrypt() should not be garbled""" % (self.module_name,) - - def runTest(self): - - ## ECB mode - mode = self.module.MODE_ECB - encryption_cipher = self.module.new(a2b_hex(self.key), mode) - ciphertext = encryption_cipher.encrypt(self.plaintext) - decryption_cipher = self.module.new(a2b_hex(self.key), mode) - decrypted_plaintext = decryption_cipher.decrypt(ciphertext) - self.assertEqual(self.plaintext, decrypted_plaintext) - - -class IVLengthTest(unittest.TestCase): - def __init__(self, module, params): - unittest.TestCase.__init__(self) - self.module = module - self.key = b(params['key']) - - def shortDescription(self): - return "Check that all modes except MODE_ECB and MODE_CTR require an IV of the proper length" - - def runTest(self): - self.assertRaises(TypeError, self.module.new, a2b_hex(self.key), - self.module.MODE_ECB, b("")) - - def _dummy_counter(self): - return "\0" * self.module.block_size - - -class NoDefaultECBTest(unittest.TestCase): - def __init__(self, module, params): - unittest.TestCase.__init__(self) - self.module = module - self.key = b(params['key']) - - def runTest(self): - self.assertRaises(TypeError, self.module.new, a2b_hex(self.key)) - - -class BlockSizeTest(unittest.TestCase): - def __init__(self, module, params): - unittest.TestCase.__init__(self) - self.module = module - self.key = a2b_hex(b(params['key'])) - - def runTest(self): - cipher = self.module.new(self.key, self.module.MODE_ECB) - self.assertEqual(cipher.block_size, self.module.block_size) - - -class ByteArrayTest(unittest.TestCase): - """Verify we can use bytearray's for encrypting and decrypting""" - - def __init__(self, module, params): - unittest.TestCase.__init__(self) - self.module = module - - # Extract the parameters - params = params.copy() - self.description = _extract(params, 'description') - self.key = b(_extract(params, 'key')) - self.plaintext = b(_extract(params, 'plaintext')) - self.ciphertext = b(_extract(params, 'ciphertext')) - self.module_name = _extract(params, 'module_name', None) - self.assoc_data = _extract(params, 'assoc_data', None) - self.mac = _extract(params, 'mac', None) - if self.assoc_data: - self.mac = b(self.mac) - - mode = _extract(params, 'mode', None) - self.mode_name = str(mode) - - if mode is not None: - # Block cipher - self.mode = getattr(self.module, "MODE_" + mode) - - self.iv = _extract(params, 'iv', None) - if self.iv is None: - self.iv = _extract(params, 'nonce', None) - if self.iv is not None: - self.iv = b(self.iv) - else: - # Stream cipher - self.mode = None - self.iv = _extract(params, 'iv', None) - if self.iv is not None: - self.iv = b(self.iv) - - self.extra_params = params - - def _new(self): - params = self.extra_params.copy() - key = a2b_hex(self.key) - - old_style = [] - if self.mode is not None: - old_style = [ self.mode ] - if self.iv is not None: - old_style += [ a2b_hex(self.iv) ] - - return self.module.new(key, *old_style, **params) - - def runTest(self): - - plaintext = a2b_hex(self.plaintext) - ciphertext = a2b_hex(self.ciphertext) - assoc_data = [] - if self.assoc_data: - assoc_data = [ bytearray(a2b_hex(b(x))) for x in self.assoc_data] - - cipher = self._new() - decipher = self._new() - - # Only AEAD modes - for comp in assoc_data: - cipher.update(comp) - decipher.update(comp) - - ct = b2a_hex(cipher.encrypt(bytearray(plaintext))) - pt = b2a_hex(decipher.decrypt(bytearray(ciphertext))) - - self.assertEqual(self.ciphertext, ct) # encrypt - self.assertEqual(self.plaintext, pt) # decrypt - - if self.mac: - mac = b2a_hex(cipher.digest()) - self.assertEqual(self.mac, mac) - decipher.verify(bytearray(a2b_hex(self.mac))) - - -class MemoryviewTest(unittest.TestCase): - """Verify we can use memoryviews for encrypting and decrypting""" - - def __init__(self, module, params): - unittest.TestCase.__init__(self) - self.module = module - - # Extract the parameters - params = params.copy() - self.description = _extract(params, 'description') - self.key = b(_extract(params, 'key')) - self.plaintext = b(_extract(params, 'plaintext')) - self.ciphertext = b(_extract(params, 'ciphertext')) - self.module_name = _extract(params, 'module_name', None) - self.assoc_data = _extract(params, 'assoc_data', None) - self.mac = _extract(params, 'mac', None) - if self.assoc_data: - self.mac = b(self.mac) - - mode = _extract(params, 'mode', None) - self.mode_name = str(mode) - - if mode is not None: - # Block cipher - self.mode = getattr(self.module, "MODE_" + mode) - - self.iv = _extract(params, 'iv', None) - if self.iv is None: - self.iv = _extract(params, 'nonce', None) - if self.iv is not None: - self.iv = b(self.iv) - else: - # Stream cipher - self.mode = None - self.iv = _extract(params, 'iv', None) - if self.iv is not None: - self.iv = b(self.iv) - - self.extra_params = params - - def _new(self): - params = self.extra_params.copy() - key = a2b_hex(self.key) - - old_style = [] - if self.mode is not None: - old_style = [ self.mode ] - if self.iv is not None: - old_style += [ a2b_hex(self.iv) ] - - return self.module.new(key, *old_style, **params) - - def runTest(self): - - plaintext = a2b_hex(self.plaintext) - ciphertext = a2b_hex(self.ciphertext) - assoc_data = [] - if self.assoc_data: - assoc_data = [ memoryview(a2b_hex(b(x))) for x in self.assoc_data] - - cipher = self._new() - decipher = self._new() - - # Only AEAD modes - for comp in assoc_data: - cipher.update(comp) - decipher.update(comp) - - ct = b2a_hex(cipher.encrypt(memoryview(plaintext))) - pt = b2a_hex(decipher.decrypt(memoryview(ciphertext))) - - self.assertEqual(self.ciphertext, ct) # encrypt - self.assertEqual(self.plaintext, pt) # decrypt - - if self.mac: - mac = b2a_hex(cipher.digest()) - self.assertEqual(self.mac, mac) - decipher.verify(memoryview(a2b_hex(self.mac))) - - -def make_block_tests(module, module_name, test_data, additional_params=dict()): - tests = [] - extra_tests_added = False - for i in range(len(test_data)): - row = test_data[i] - - # Build the "params" dictionary with - # - plaintext - # - ciphertext - # - key - # - mode (default is ECB) - # - (optionally) description - # - (optionally) any other parameter that this cipher mode requires - params = {} - if len(row) == 3: - (params['plaintext'], params['ciphertext'], params['key']) = row - elif len(row) == 4: - (params['plaintext'], params['ciphertext'], params['key'], params['description']) = row - elif len(row) == 5: - (params['plaintext'], params['ciphertext'], params['key'], params['description'], extra_params) = row - params.update(extra_params) - else: - raise AssertionError("Unsupported tuple size %d" % (len(row),)) - - if not "mode" in params: - params["mode"] = "ECB" - - # Build the display-name for the test - p2 = params.copy() - p_key = _extract(p2, 'key') - p_plaintext = _extract(p2, 'plaintext') - p_ciphertext = _extract(p2, 'ciphertext') - p_mode = _extract(p2, 'mode') - p_description = _extract(p2, 'description', None) - - if p_description is not None: - description = p_description - elif p_mode == 'ECB' and not p2: - description = "p=%s, k=%s" % (p_plaintext, p_key) - else: - description = "p=%s, k=%s, %r" % (p_plaintext, p_key, p2) - name = "%s #%d: %s" % (module_name, i+1, description) - params['description'] = name - params['module_name'] = module_name - params.update(additional_params) - - # Add extra test(s) to the test suite before the current test - if not extra_tests_added: - tests += [ - RoundtripTest(module, params), - IVLengthTest(module, params), - NoDefaultECBTest(module, params), - ByteArrayTest(module, params), - BlockSizeTest(module, params), - ] - extra_tests_added = True - - # Add the current test to the test suite - tests.append(CipherSelfTest(module, params)) - - return tests - -def make_stream_tests(module, module_name, test_data): - tests = [] - extra_tests_added = False - for i in range(len(test_data)): - row = test_data[i] - - # Build the "params" dictionary - params = {} - if len(row) == 3: - (params['plaintext'], params['ciphertext'], params['key']) = row - elif len(row) == 4: - (params['plaintext'], params['ciphertext'], params['key'], params['description']) = row - elif len(row) == 5: - (params['plaintext'], params['ciphertext'], params['key'], params['description'], extra_params) = row - params.update(extra_params) - else: - raise AssertionError("Unsupported tuple size %d" % (len(row),)) - - # Build the display-name for the test - p2 = params.copy() - p_key = _extract(p2, 'key') - p_plaintext = _extract(p2, 'plaintext') - p_ciphertext = _extract(p2, 'ciphertext') - p_description = _extract(p2, 'description', None) - - if p_description is not None: - description = p_description - elif not p2: - description = "p=%s, k=%s" % (p_plaintext, p_key) - else: - description = "p=%s, k=%s, %r" % (p_plaintext, p_key, p2) - name = "%s #%d: %s" % (module_name, i+1, description) - params['description'] = name - params['module_name'] = module_name - - # Add extra test(s) to the test suite before the current test - if not extra_tests_added: - tests += [ - ByteArrayTest(module, params), - ] - - tests.append(MemoryviewTest(module, params)) - extra_tests_added = True - - # Add the test to the test suite - tests.append(CipherSelfTest(module, params)) - tests.append(CipherStreamingSelfTest(module, params)) - return tests - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_AES.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_AES.py deleted file mode 100644 index bd6c40e..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_AES.py +++ /dev/null @@ -1,1351 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Cipher/AES.py: Self-test for the AES cipher -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Cipher.AES""" - -from __future__ import print_function - -import unittest -from Cryptodome.Hash import SHA256 -from Cryptodome.Cipher import AES -from Cryptodome.Util.py3compat import * -from binascii import hexlify - -# This is a list of (plaintext, ciphertext, key[, description[, params]]) tuples. -test_data = [ - # FIPS PUB 197 test vectors - # http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf - - ('00112233445566778899aabbccddeeff', '69c4e0d86a7b0430d8cdb78070b4c55a', - '000102030405060708090a0b0c0d0e0f', 'FIPS 197 C.1 (AES-128)'), - - ('00112233445566778899aabbccddeeff', 'dda97ca4864cdfe06eaf70a0ec0d7191', - '000102030405060708090a0b0c0d0e0f1011121314151617', - 'FIPS 197 C.2 (AES-192)'), - - ('00112233445566778899aabbccddeeff', '8ea2b7ca516745bfeafc49904b496089', - '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', - 'FIPS 197 C.3 (AES-256)'), - - # Rijndael128 test vectors - # Downloaded 2008-09-13 from - # http://www.iaik.tugraz.at/Research/krypto/AES/old/~rijmen/rijndael/testvalues.tar.gz - - # ecb_tbl.txt, KEYSIZE=128 - ('506812a45f08c889b97f5980038b8359', 'd8f532538289ef7d06b506a4fd5be9c9', - '00010203050607080a0b0c0d0f101112', - 'ecb-tbl-128: I=1'), - ('5c6d71ca30de8b8b00549984d2ec7d4b', '59ab30f4d4ee6e4ff9907ef65b1fb68c', - '14151617191a1b1c1e1f202123242526', - 'ecb-tbl-128: I=2'), - ('53f3f4c64f8616e4e7c56199f48f21f6', 'bf1ed2fcb2af3fd41443b56d85025cb1', - '28292a2b2d2e2f30323334353738393a', - 'ecb-tbl-128: I=3'), - ('a1eb65a3487165fb0f1c27ff9959f703', '7316632d5c32233edcb0780560eae8b2', - '3c3d3e3f41424344464748494b4c4d4e', - 'ecb-tbl-128: I=4'), - ('3553ecf0b1739558b08e350a98a39bfa', '408c073e3e2538072b72625e68b8364b', - '50515253555657585a5b5c5d5f606162', - 'ecb-tbl-128: I=5'), - ('67429969490b9711ae2b01dc497afde8', 'e1f94dfa776597beaca262f2f6366fea', - '64656667696a6b6c6e6f707173747576', - 'ecb-tbl-128: I=6'), - ('93385c1f2aec8bed192f5a8e161dd508', 'f29e986c6a1c27d7b29ffd7ee92b75f1', - '78797a7b7d7e7f80828384858788898a', - 'ecb-tbl-128: I=7'), - ('b5bf946be19beb8db3983b5f4c6e8ddb', '131c886a57f8c2e713aba6955e2b55b5', - '8c8d8e8f91929394969798999b9c9d9e', - 'ecb-tbl-128: I=8'), - ('41321ee10e21bd907227c4450ff42324', 'd2ab7662df9b8c740210e5eeb61c199d', - 'a0a1a2a3a5a6a7a8aaabacadafb0b1b2', - 'ecb-tbl-128: I=9'), - ('00a82f59c91c8486d12c0a80124f6089', '14c10554b2859c484cab5869bbe7c470', - 'b4b5b6b7b9babbbcbebfc0c1c3c4c5c6', - 'ecb-tbl-128: I=10'), - ('7ce0fd076754691b4bbd9faf8a1372fe', 'db4d498f0a49cf55445d502c1f9ab3b5', - 'c8c9cacbcdcecfd0d2d3d4d5d7d8d9da', - 'ecb-tbl-128: I=11'), - ('23605a8243d07764541bc5ad355b3129', '6d96fef7d66590a77a77bb2056667f7f', - 'dcdddedfe1e2e3e4e6e7e8e9ebecedee', - 'ecb-tbl-128: I=12'), - ('12a8cfa23ea764fd876232b4e842bc44', '316fb68edba736c53e78477bf913725c', - 'f0f1f2f3f5f6f7f8fafbfcfdfe010002', - 'ecb-tbl-128: I=13'), - ('bcaf32415e8308b3723e5fdd853ccc80', '6936f2b93af8397fd3a771fc011c8c37', - '04050607090a0b0c0e0f101113141516', - 'ecb-tbl-128: I=14'), - ('89afae685d801ad747ace91fc49adde0', 'f3f92f7a9c59179c1fcc2c2ba0b082cd', - '2c2d2e2f31323334363738393b3c3d3e', - 'ecb-tbl-128: I=15'), - ('f521d07b484357c4a69e76124a634216', '6a95ea659ee3889158e7a9152ff04ebc', - '40414243454647484a4b4c4d4f505152', - 'ecb-tbl-128: I=16'), - ('3e23b3bc065bcc152407e23896d77783', '1959338344e945670678a5d432c90b93', - '54555657595a5b5c5e5f606163646566', - 'ecb-tbl-128: I=17'), - ('79f0fba002be1744670e7e99290d8f52', 'e49bddd2369b83ee66e6c75a1161b394', - '68696a6b6d6e6f70727374757778797a', - 'ecb-tbl-128: I=18'), - ('da23fe9d5bd63e1d72e3dafbe21a6c2a', 'd3388f19057ff704b70784164a74867d', - '7c7d7e7f81828384868788898b8c8d8e', - 'ecb-tbl-128: I=19'), - ('e3f5698ba90b6a022efd7db2c7e6c823', '23aa03e2d5e4cd24f3217e596480d1e1', - 'a4a5a6a7a9aaabacaeafb0b1b3b4b5b6', - 'ecb-tbl-128: I=20'), - ('bdc2691d4f1b73d2700679c3bcbf9c6e', 'c84113d68b666ab2a50a8bdb222e91b9', - 'e0e1e2e3e5e6e7e8eaebecedeff0f1f2', - 'ecb-tbl-128: I=21'), - ('ba74e02093217ee1ba1b42bd5624349a', 'ac02403981cd4340b507963db65cb7b6', - '08090a0b0d0e0f10121314151718191a', - 'ecb-tbl-128: I=22'), - ('b5c593b5851c57fbf8b3f57715e8f680', '8d1299236223359474011f6bf5088414', - '6c6d6e6f71727374767778797b7c7d7e', - 'ecb-tbl-128: I=23'), - ('3da9bd9cec072381788f9387c3bbf4ee', '5a1d6ab8605505f7977e55b9a54d9b90', - '80818283858687888a8b8c8d8f909192', - 'ecb-tbl-128: I=24'), - ('4197f3051121702ab65d316b3c637374', '72e9c2d519cf555e4208805aabe3b258', - '94959697999a9b9c9e9fa0a1a3a4a5a6', - 'ecb-tbl-128: I=25'), - ('9f46c62ec4f6ee3f6e8c62554bc48ab7', 'a8f3e81c4a23a39ef4d745dffe026e80', - 'a8a9aaabadaeafb0b2b3b4b5b7b8b9ba', - 'ecb-tbl-128: I=26'), - ('0220673fe9e699a4ebc8e0dbeb6979c8', '546f646449d31458f9eb4ef5483aee6c', - 'bcbdbebfc1c2c3c4c6c7c8c9cbcccdce', - 'ecb-tbl-128: I=27'), - ('b2b99171337ded9bc8c2c23ff6f18867', '4dbe4bc84ac797c0ee4efb7f1a07401c', - 'd0d1d2d3d5d6d7d8dadbdcdddfe0e1e2', - 'ecb-tbl-128: I=28'), - ('a7facf4e301e984e5efeefd645b23505', '25e10bfb411bbd4d625ac8795c8ca3b3', - 'e4e5e6e7e9eaebeceeeff0f1f3f4f5f6', - 'ecb-tbl-128: I=29'), - ('f7c762e4a9819160fd7acfb6c4eedcdd', '315637405054ec803614e43def177579', - 'f8f9fafbfdfefe00020304050708090a', - 'ecb-tbl-128: I=30'), - ('9b64fc21ea08709f4915436faa70f1be', '60c5bc8a1410247295c6386c59e572a8', - '0c0d0e0f11121314161718191b1c1d1e', - 'ecb-tbl-128: I=31'), - ('52af2c3de07ee6777f55a4abfc100b3f', '01366fc8ca52dfe055d6a00a76471ba6', - '20212223252627282a2b2c2d2f303132', - 'ecb-tbl-128: I=32'), - ('2fca001224386c57aa3f968cbe2c816f', 'ecc46595516ec612449c3f581e7d42ff', - '34353637393a3b3c3e3f404143444546', - 'ecb-tbl-128: I=33'), - ('4149c73658a4a9c564342755ee2c132f', '6b7ffe4c602a154b06ee9c7dab5331c9', - '48494a4b4d4e4f50525354555758595a', - 'ecb-tbl-128: I=34'), - ('af60005a00a1772f7c07a48a923c23d2', '7da234c14039a240dd02dd0fbf84eb67', - '5c5d5e5f61626364666768696b6c6d6e', - 'ecb-tbl-128: I=35'), - ('6fccbc28363759914b6f0280afaf20c6', 'c7dc217d9e3604ffe7e91f080ecd5a3a', - '70717273757677787a7b7c7d7f808182', - 'ecb-tbl-128: I=36'), - ('7d82a43ddf4fefa2fc5947499884d386', '37785901863f5c81260ea41e7580cda5', - '84858687898a8b8c8e8f909193949596', - 'ecb-tbl-128: I=37'), - ('5d5a990eaab9093afe4ce254dfa49ef9', 'a07b9338e92ed105e6ad720fccce9fe4', - '98999a9b9d9e9fa0a2a3a4a5a7a8a9aa', - 'ecb-tbl-128: I=38'), - ('4cd1e2fd3f4434b553aae453f0ed1a02', 'ae0fb9722418cc21a7da816bbc61322c', - 'acadaeafb1b2b3b4b6b7b8b9bbbcbdbe', - 'ecb-tbl-128: I=39'), - ('5a2c9a9641d4299125fa1b9363104b5e', 'c826a193080ff91ffb21f71d3373c877', - 'c0c1c2c3c5c6c7c8cacbcccdcfd0d1d2', - 'ecb-tbl-128: I=40'), - ('b517fe34c0fa217d341740bfd4fe8dd4', '1181b11b0e494e8d8b0aa6b1d5ac2c48', - 'd4d5d6d7d9dadbdcdedfe0e1e3e4e5e6', - 'ecb-tbl-128: I=41'), - ('014baf2278a69d331d5180103643e99a', '6743c3d1519ab4f2cd9a78ab09a511bd', - 'e8e9eaebedeeeff0f2f3f4f5f7f8f9fa', - 'ecb-tbl-128: I=42'), - ('b529bd8164f20d0aa443d4932116841c', 'dc55c076d52bacdf2eefd952946a439d', - 'fcfdfeff01020304060708090b0c0d0e', - 'ecb-tbl-128: I=43'), - ('2e596dcbb2f33d4216a1176d5bd1e456', '711b17b590ffc72b5c8e342b601e8003', - '10111213151617181a1b1c1d1f202122', - 'ecb-tbl-128: I=44'), - ('7274a1ea2b7ee2424e9a0e4673689143', '19983bb0950783a537e1339f4aa21c75', - '24252627292a2b2c2e2f303133343536', - 'ecb-tbl-128: I=45'), - ('ae20020bd4f13e9d90140bee3b5d26af', '3ba7762e15554169c0f4fa39164c410c', - '38393a3b3d3e3f40424344454748494a', - 'ecb-tbl-128: I=46'), - ('baac065da7ac26e855e79c8849d75a02', 'a0564c41245afca7af8aa2e0e588ea89', - '4c4d4e4f51525354565758595b5c5d5e', - 'ecb-tbl-128: I=47'), - ('7c917d8d1d45fab9e2540e28832540cc', '5e36a42a2e099f54ae85ecd92e2381ed', - '60616263656667686a6b6c6d6f707172', - 'ecb-tbl-128: I=48'), - ('bde6f89e16daadb0e847a2a614566a91', '770036f878cd0f6ca2268172f106f2fe', - '74757677797a7b7c7e7f808183848586', - 'ecb-tbl-128: I=49'), - ('c9de163725f1f5be44ebb1db51d07fbc', '7e4e03908b716116443ccf7c94e7c259', - '88898a8b8d8e8f90929394959798999a', - 'ecb-tbl-128: I=50'), - ('3af57a58f0c07dffa669572b521e2b92', '482735a48c30613a242dd494c7f9185d', - '9c9d9e9fa1a2a3a4a6a7a8a9abacadae', - 'ecb-tbl-128: I=51'), - ('3d5ebac306dde4604f1b4fbbbfcdae55', 'b4c0f6c9d4d7079addf9369fc081061d', - 'b0b1b2b3b5b6b7b8babbbcbdbfc0c1c2', - 'ecb-tbl-128: I=52'), - ('c2dfa91bceb76a1183c995020ac0b556', 'd5810fe0509ac53edcd74f89962e6270', - 'c4c5c6c7c9cacbcccecfd0d1d3d4d5d6', - 'ecb-tbl-128: I=53'), - ('c70f54305885e9a0746d01ec56c8596b', '03f17a16b3f91848269ecdd38ebb2165', - 'd8d9dadbdddedfe0e2e3e4e5e7e8e9ea', - 'ecb-tbl-128: I=54'), - ('c4f81b610e98012ce000182050c0c2b2', 'da1248c3180348bad4a93b4d9856c9df', - 'ecedeeeff1f2f3f4f6f7f8f9fbfcfdfe', - 'ecb-tbl-128: I=55'), - ('eaab86b1d02a95d7404eff67489f97d4', '3d10d7b63f3452c06cdf6cce18be0c2c', - '00010203050607080a0b0c0d0f101112', - 'ecb-tbl-128: I=56'), - ('7c55bdb40b88870b52bec3738de82886', '4ab823e7477dfddc0e6789018fcb6258', - '14151617191a1b1c1e1f202123242526', - 'ecb-tbl-128: I=57'), - ('ba6eaa88371ff0a3bd875e3f2a975ce0', 'e6478ba56a77e70cfdaa5c843abde30e', - '28292a2b2d2e2f30323334353738393a', - 'ecb-tbl-128: I=58'), - ('08059130c4c24bd30cf0575e4e0373dc', '1673064895fbeaf7f09c5429ff75772d', - '3c3d3e3f41424344464748494b4c4d4e', - 'ecb-tbl-128: I=59'), - ('9a8eab004ef53093dfcf96f57e7eda82', '4488033ae9f2efd0ca9383bfca1a94e9', - '50515253555657585a5b5c5d5f606162', - 'ecb-tbl-128: I=60'), - ('0745b589e2400c25f117b1d796c28129', '978f3b8c8f9d6f46626cac3c0bcb9217', - '64656667696a6b6c6e6f707173747576', - 'ecb-tbl-128: I=61'), - ('2f1777781216cec3f044f134b1b92bbe', 'e08c8a7e582e15e5527f1d9e2eecb236', - '78797a7b7d7e7f80828384858788898a', - 'ecb-tbl-128: I=62'), - ('353a779ffc541b3a3805d90ce17580fc', 'cec155b76ac5ffda4cf4f9ca91e49a7a', - '8c8d8e8f91929394969798999b9c9d9e', - 'ecb-tbl-128: I=63'), - ('1a1eae4415cefcf08c4ac1c8f68bea8f', 'd5ac7165763225dd2a38cdc6862c29ad', - 'a0a1a2a3a5a6a7a8aaabacadafb0b1b2', - 'ecb-tbl-128: I=64'), - ('e6e7e4e5b0b3b2b5d4d5aaab16111013', '03680fe19f7ce7275452020be70e8204', - 'b4b5b6b7b9babbbcbebfc0c1c3c4c5c6', - 'ecb-tbl-128: I=65'), - ('f8f9fafbfbf8f9e677767170efe0e1e2', '461df740c9781c388e94bb861ceb54f6', - 'c8c9cacbcdcecfd0d2d3d4d5d7d8d9da', - 'ecb-tbl-128: I=66'), - ('63626160a1a2a3a445444b4a75727370', '451bd60367f96483042742219786a074', - 'dcdddedfe1e2e3e4e6e7e8e9ebecedee', - 'ecb-tbl-128: I=67'), - ('717073720605040b2d2c2b2a05fafbf9', 'e4dfa42671a02e57ef173b85c0ea9f2b', - 'f0f1f2f3f5f6f7f8fafbfcfdfe010002', - 'ecb-tbl-128: I=68'), - ('78797a7beae9e8ef3736292891969794', 'ed11b89e76274282227d854700a78b9e', - '04050607090a0b0c0e0f101113141516', - 'ecb-tbl-128: I=69'), - ('838281803231300fdddcdbdaa0afaead', '433946eaa51ea47af33895f2b90b3b75', - '18191a1b1d1e1f20222324252728292a', - 'ecb-tbl-128: I=70'), - ('18191a1bbfbcbdba75747b7a7f78797a', '6bc6d616a5d7d0284a5910ab35022528', - '2c2d2e2f31323334363738393b3c3d3e', - 'ecb-tbl-128: I=71'), - ('848586879b989996a3a2a5a4849b9a99', 'd2a920ecfe919d354b5f49eae9719c98', - '40414243454647484a4b4c4d4f505152', - 'ecb-tbl-128: I=72'), - ('0001020322212027cacbf4f551565754', '3a061b17f6a92885efbd0676985b373d', - '54555657595a5b5c5e5f606163646566', - 'ecb-tbl-128: I=73'), - ('cecfcccdafacadb2515057564a454447', 'fadeec16e33ea2f4688499d157e20d8f', - '68696a6b6d6e6f70727374757778797a', - 'ecb-tbl-128: I=74'), - ('92939091cdcecfc813121d1c80878685', '5cdefede59601aa3c3cda36fa6b1fa13', - '7c7d7e7f81828384868788898b8c8d8e', - 'ecb-tbl-128: I=75'), - ('d2d3d0d16f6c6d6259585f5ed1eeefec', '9574b00039844d92ebba7ee8719265f8', - '90919293959697989a9b9c9d9fa0a1a2', - 'ecb-tbl-128: I=76'), - ('acadaeaf878485820f0e1110d5d2d3d0', '9a9cf33758671787e5006928188643fa', - 'a4a5a6a7a9aaabacaeafb0b1b3b4b5b6', - 'ecb-tbl-128: I=77'), - ('9091929364676619e6e7e0e1757a7b78', '2cddd634c846ba66bb46cbfea4a674f9', - 'b8b9babbbdbebfc0c2c3c4c5c7c8c9ca', - 'ecb-tbl-128: I=78'), - ('babbb8b98a89888f74757a7b92959497', 'd28bae029393c3e7e26e9fafbbb4b98f', - 'cccdcecfd1d2d3d4d6d7d8d9dbdcddde', - 'ecb-tbl-128: I=79'), - ('8d8c8f8e6e6d6c633b3a3d3ccad5d4d7', 'ec27529b1bee0a9ab6a0d73ebc82e9b7', - 'e0e1e2e3e5e6e7e8eaebecedeff0f1f2', - 'ecb-tbl-128: I=80'), - ('86878485010203040808f7f767606162', '3cb25c09472aff6ee7e2b47ccd7ccb17', - 'f4f5f6f7f9fafbfcfefe010103040506', - 'ecb-tbl-128: I=81'), - ('8e8f8c8d656667788a8b8c8d010e0f0c', 'dee33103a7283370d725e44ca38f8fe5', - '08090a0b0d0e0f10121314151718191a', - 'ecb-tbl-128: I=82'), - ('c8c9cacb858687807a7b7475e7e0e1e2', '27f9bcd1aac64bffc11e7815702c1a69', - '1c1d1e1f21222324262728292b2c2d2e', - 'ecb-tbl-128: I=83'), - ('6d6c6f6e5053525d8c8d8a8badd2d3d0', '5df534ffad4ed0749a9988e9849d0021', - '30313233353637383a3b3c3d3f404142', - 'ecb-tbl-128: I=84'), - ('28292a2b393a3b3c0607181903040506', 'a48bee75db04fb60ca2b80f752a8421b', - '44454647494a4b4c4e4f505153545556', - 'ecb-tbl-128: I=85'), - ('a5a4a7a6b0b3b28ddbdadddcbdb2b3b0', '024c8cf70bc86ee5ce03678cb7af45f9', - '58595a5b5d5e5f60626364656768696a', - 'ecb-tbl-128: I=86'), - ('323330316467666130313e3f2c2b2a29', '3c19ac0f8a3a3862ce577831301e166b', - '6c6d6e6f71727374767778797b7c7d7e', - 'ecb-tbl-128: I=87'), - ('27262524080b0a05171611100b141516', 'c5e355b796a57421d59ca6be82e73bca', - '80818283858687888a8b8c8d8f909192', - 'ecb-tbl-128: I=88'), - ('040506074142434435340b0aa3a4a5a6', 'd94033276417abfb05a69d15b6e386e2', - '94959697999a9b9c9e9fa0a1a3a4a5a6', - 'ecb-tbl-128: I=89'), - ('242526271112130c61606766bdb2b3b0', '24b36559ea3a9b9b958fe6da3e5b8d85', - 'a8a9aaabadaeafb0b2b3b4b5b7b8b9ba', - 'ecb-tbl-128: I=90'), - ('4b4a4948252627209e9f9091cec9c8cb', '20fd4feaa0e8bf0cce7861d74ef4cb72', - 'bcbdbebfc1c2c3c4c6c7c8c9cbcccdce', - 'ecb-tbl-128: I=91'), - ('68696a6b6665646b9f9e9998d9e6e7e4', '350e20d5174277b9ec314c501570a11d', - 'd0d1d2d3d5d6d7d8dadbdcdddfe0e1e2', - 'ecb-tbl-128: I=92'), - ('34353637c5c6c7c0f0f1eeef7c7b7a79', '87a29d61b7c604d238fe73045a7efd57', - 'e4e5e6e7e9eaebeceeeff0f1f3f4f5f6', - 'ecb-tbl-128: I=93'), - ('32333031c2c1c13f0d0c0b0a050a0b08', '2c3164c1cc7d0064816bdc0faa362c52', - 'f8f9fafbfdfefe00020304050708090a', - 'ecb-tbl-128: I=94'), - ('cdcccfcebebdbcbbabaaa5a4181f1e1d', '195fe5e8a05a2ed594f6e4400eee10b3', - '0c0d0e0f11121314161718191b1c1d1e', - 'ecb-tbl-128: I=95'), - ('212023223635343ba0a1a6a7445b5a59', 'e4663df19b9a21a5a284c2bd7f905025', - '20212223252627282a2b2c2d2f303132', - 'ecb-tbl-128: I=96'), - ('0e0f0c0da8abaaad2f2e515002050407', '21b88714cfb4e2a933bd281a2c4743fd', - '34353637393a3b3c3e3f404143444546', - 'ecb-tbl-128: I=97'), - ('070605042a2928378e8f8889bdb2b3b0', 'cbfc3980d704fd0fc54378ab84e17870', - '48494a4b4d4e4f50525354555758595a', - 'ecb-tbl-128: I=98'), - ('cbcac9c893909196a9a8a7a6a5a2a3a0', 'bc5144baa48bdeb8b63e22e03da418ef', - '5c5d5e5f61626364666768696b6c6d6e', - 'ecb-tbl-128: I=99'), - ('80818283c1c2c3cc9c9d9a9b0cf3f2f1', '5a1dbaef1ee2984b8395da3bdffa3ccc', - '70717273757677787a7b7c7d7f808182', - 'ecb-tbl-128: I=100'), - ('1213101125262720fafbe4e5b1b6b7b4', 'f0b11cd0729dfcc80cec903d97159574', - '84858687898a8b8c8e8f909193949596', - 'ecb-tbl-128: I=101'), - ('7f7e7d7c3033320d97969190222d2c2f', '9f95314acfddc6d1914b7f19a9cc8209', - '98999a9b9d9e9fa0a2a3a4a5a7a8a9aa', - 'ecb-tbl-128: I=102'), - ('4e4f4c4d484b4a4d81808f8e53545556', '595736f6f0f70914a94e9e007f022519', - 'acadaeafb1b2b3b4b6b7b8b9bbbcbdbe', - 'ecb-tbl-128: I=103'), - ('dcdddedfb0b3b2bd15141312a1bebfbc', '1f19f57892cae586fcdfb4c694deb183', - 'c0c1c2c3c5c6c7c8cacbcccdcfd0d1d2', - 'ecb-tbl-128: I=104'), - ('93929190282b2a2dc4c5fafb92959497', '540700ee1f6f3dab0b3eddf6caee1ef5', - 'd4d5d6d7d9dadbdcdedfe0e1e3e4e5e6', - 'ecb-tbl-128: I=105'), - ('f5f4f7f6c4c7c6d9373631307e717073', '14a342a91019a331687a2254e6626ca2', - 'e8e9eaebedeeeff0f2f3f4f5f7f8f9fa', - 'ecb-tbl-128: I=106'), - ('93929190b6b5b4b364656a6b05020300', '7b25f3c3b2eea18d743ef283140f29ff', - 'fcfdfeff01020304060708090b0c0d0e', - 'ecb-tbl-128: I=107'), - ('babbb8b90d0e0f00a4a5a2a3043b3a39', '46c2587d66e5e6fa7f7ca6411ad28047', - '10111213151617181a1b1c1d1f202122', - 'ecb-tbl-128: I=108'), - ('d8d9dadb7f7c7d7a10110e0f787f7e7d', '09470e72229d954ed5ee73886dfeeba9', - '24252627292a2b2c2e2f303133343536', - 'ecb-tbl-128: I=109'), - ('fefffcfdefeced923b3a3d3c6768696a', 'd77c03de92d4d0d79ef8d4824ef365eb', - '38393a3b3d3e3f40424344454748494a', - 'ecb-tbl-128: I=110'), - ('d6d7d4d58a89888f96979899a5a2a3a0', '1d190219f290e0f1715d152d41a23593', - '4c4d4e4f51525354565758595b5c5d5e', - 'ecb-tbl-128: I=111'), - ('18191a1ba8abaaa5303136379b848586', 'a2cd332ce3a0818769616292e87f757b', - '60616263656667686a6b6c6d6f707172', - 'ecb-tbl-128: I=112'), - ('6b6a6968a4a7a6a1d6d72829b0b7b6b5', 'd54afa6ce60fbf9341a3690e21385102', - '74757677797a7b7c7e7f808183848586', - 'ecb-tbl-128: I=113'), - ('000102038a89889755545352a6a9a8ab', '06e5c364ded628a3f5e05e613e356f46', - '88898a8b8d8e8f90929394959798999a', - 'ecb-tbl-128: I=114'), - ('2d2c2f2eb3b0b1b6b6b7b8b9f2f5f4f7', 'eae63c0e62556dac85d221099896355a', - '9c9d9e9fa1a2a3a4a6a7a8a9abacadae', - 'ecb-tbl-128: I=115'), - ('979695943536373856575051e09f9e9d', '1fed060e2c6fc93ee764403a889985a2', - 'b0b1b2b3b5b6b7b8babbbcbdbfc0c1c2', - 'ecb-tbl-128: I=116'), - ('a4a5a6a7989b9a9db1b0afae7a7d7c7f', 'c25235c1a30fdec1c7cb5c5737b2a588', - 'c4c5c6c7c9cacbcccecfd0d1d3d4d5d6', - 'ecb-tbl-128: I=117'), - ('c1c0c3c2686b6a55a8a9aeafeae5e4e7', '796dbef95147d4d30873ad8b7b92efc0', - 'd8d9dadbdddedfe0e2e3e4e5e7e8e9ea', - 'ecb-tbl-128: I=118'), - ('c1c0c3c2141716118c8d828364636261', 'cbcf0fb34d98d0bd5c22ce37211a46bf', - 'ecedeeeff1f2f3f4f6f7f8f9fbfcfdfe', - 'ecb-tbl-128: I=119'), - ('93929190cccfcec196979091e0fffefd', '94b44da6466126cafa7c7fd09063fc24', - '00010203050607080a0b0c0d0f101112', - 'ecb-tbl-128: I=120'), - ('b4b5b6b7f9fafbfc25241b1a6e69686b', 'd78c5b5ebf9b4dbda6ae506c5074c8fe', - '14151617191a1b1c1e1f202123242526', - 'ecb-tbl-128: I=121'), - ('868784850704051ac7c6c1c08788898a', '6c27444c27204b043812cf8cf95f9769', - '28292a2b2d2e2f30323334353738393a', - 'ecb-tbl-128: I=122'), - ('f4f5f6f7aaa9a8affdfcf3f277707172', 'be94524ee5a2aa50bba8b75f4c0aebcf', - '3c3d3e3f41424344464748494b4c4d4e', - 'ecb-tbl-128: I=123'), - ('d3d2d1d00605040bc3c2c5c43e010003', 'a0aeaae91ba9f31f51aeb3588cf3a39e', - '50515253555657585a5b5c5d5f606162', - 'ecb-tbl-128: I=124'), - ('73727170424140476a6b74750d0a0b08', '275297779c28266ef9fe4c6a13c08488', - '64656667696a6b6c6e6f707173747576', - 'ecb-tbl-128: I=125'), - ('c2c3c0c10a0908f754555253a1aeafac', '86523d92bb8672cb01cf4a77fd725882', - '78797a7b7d7e7f80828384858788898a', - 'ecb-tbl-128: I=126'), - ('6d6c6f6ef8fbfafd82838c8df8fffefd', '4b8327640e9f33322a04dd96fcbf9a36', - '8c8d8e8f91929394969798999b9c9d9e', - 'ecb-tbl-128: I=127'), - ('f5f4f7f684878689a6a7a0a1d2cdcccf', 'ce52af650d088ca559425223f4d32694', - 'a0a1a2a3a5a6a7a8aaabacadafb0b1b2', - 'ecb-tbl-128: I=128'), - - # ecb_tbl.txt, KEYSIZE=192 - ('2d33eef2c0430a8a9ebf45e809c40bb6', 'dff4945e0336df4c1c56bc700eff837f', - '00010203050607080a0b0c0d0f10111214151617191a1b1c', - 'ecb-tbl-192: I=1'), - ('6aa375d1fa155a61fb72353e0a5a8756', 'b6fddef4752765e347d5d2dc196d1252', - '1e1f20212324252628292a2b2d2e2f30323334353738393a', - 'ecb-tbl-192: I=2'), - ('bc3736518b9490dcb8ed60eb26758ed4', 'd23684e3d963b3afcf1a114aca90cbd6', - '3c3d3e3f41424344464748494b4c4d4e5051525355565758', - 'ecb-tbl-192: I=3'), - ('aa214402b46cffb9f761ec11263a311e', '3a7ac027753e2a18c2ceab9e17c11fd0', - '5a5b5c5d5f60616264656667696a6b6c6e6f707173747576', - 'ecb-tbl-192: I=4'), - ('02aea86e572eeab66b2c3af5e9a46fd6', '8f6786bd007528ba26603c1601cdd0d8', - '78797a7b7d7e7f80828384858788898a8c8d8e8f91929394', - 'ecb-tbl-192: I=5'), - ('e2aef6acc33b965c4fa1f91c75ff6f36', 'd17d073b01e71502e28b47ab551168b3', - '969798999b9c9d9ea0a1a2a3a5a6a7a8aaabacadafb0b1b2', - 'ecb-tbl-192: I=6'), - ('0659df46427162b9434865dd9499f91d', 'a469da517119fab95876f41d06d40ffa', - 'b4b5b6b7b9babbbcbebfc0c1c3c4c5c6c8c9cacbcdcecfd0', - 'ecb-tbl-192: I=7'), - ('49a44239c748feb456f59c276a5658df', '6091aa3b695c11f5c0b6ad26d3d862ff', - 'd2d3d4d5d7d8d9dadcdddedfe1e2e3e4e6e7e8e9ebecedee', - 'ecb-tbl-192: I=8'), - ('66208f6e9d04525bdedb2733b6a6be37', '70f9e67f9f8df1294131662dc6e69364', - 'f0f1f2f3f5f6f7f8fafbfcfdfe01000204050607090a0b0c', - 'ecb-tbl-192: I=9'), - ('3393f8dfc729c97f5480b950bc9666b0', 'd154dcafad8b207fa5cbc95e9996b559', - '0e0f10111314151618191a1b1d1e1f20222324252728292a', - 'ecb-tbl-192: I=10'), - ('606834c8ce063f3234cf1145325dbd71', '4934d541e8b46fa339c805a7aeb9e5da', - '2c2d2e2f31323334363738393b3c3d3e4041424345464748', - 'ecb-tbl-192: I=11'), - ('fec1c04f529bbd17d8cecfcc4718b17f', '62564c738f3efe186e1a127a0c4d3c61', - '4a4b4c4d4f50515254555657595a5b5c5e5f606163646566', - 'ecb-tbl-192: I=12'), - ('32df99b431ed5dc5acf8caf6dc6ce475', '07805aa043986eb23693e23bef8f3438', - '68696a6b6d6e6f70727374757778797a7c7d7e7f81828384', - 'ecb-tbl-192: I=13'), - ('7fdc2b746f3f665296943b83710d1f82', 'df0b4931038bade848dee3b4b85aa44b', - '868788898b8c8d8e90919293959697989a9b9c9d9fa0a1a2', - 'ecb-tbl-192: I=14'), - ('8fba1510a3c5b87e2eaa3f7a91455ca2', '592d5fded76582e4143c65099309477c', - 'a4a5a6a7a9aaabacaeafb0b1b3b4b5b6b8b9babbbdbebfc0', - 'ecb-tbl-192: I=15'), - ('2c9b468b1c2eed92578d41b0716b223b', 'c9b8d6545580d3dfbcdd09b954ed4e92', - 'c2c3c4c5c7c8c9cacccdcecfd1d2d3d4d6d7d8d9dbdcddde', - 'ecb-tbl-192: I=16'), - ('0a2bbf0efc6bc0034f8a03433fca1b1a', '5dccd5d6eb7c1b42acb008201df707a0', - 'e0e1e2e3e5e6e7e8eaebecedeff0f1f2f4f5f6f7f9fafbfc', - 'ecb-tbl-192: I=17'), - ('25260e1f31f4104d387222e70632504b', 'a2a91682ffeb6ed1d34340946829e6f9', - 'fefe01010304050608090a0b0d0e0f10121314151718191a', - 'ecb-tbl-192: I=18'), - ('c527d25a49f08a5228d338642ae65137', 'e45d185b797000348d9267960a68435d', - '1c1d1e1f21222324262728292b2c2d2e3031323335363738', - 'ecb-tbl-192: I=19'), - ('3b49fc081432f5890d0e3d87e884a69e', '45e060dae5901cda8089e10d4f4c246b', - '3a3b3c3d3f40414244454647494a4b4c4e4f505153545556', - 'ecb-tbl-192: I=20'), - ('d173f9ed1e57597e166931df2754a083', 'f6951afacc0079a369c71fdcff45df50', - '58595a5b5d5e5f60626364656768696a6c6d6e6f71727374', - 'ecb-tbl-192: I=21'), - ('8c2b7cafa5afe7f13562daeae1adede0', '9e95e00f351d5b3ac3d0e22e626ddad6', - '767778797b7c7d7e80818283858687888a8b8c8d8f909192', - 'ecb-tbl-192: I=22'), - ('aaf4ec8c1a815aeb826cab741339532c', '9cb566ff26d92dad083b51fdc18c173c', - '94959697999a9b9c9e9fa0a1a3a4a5a6a8a9aaabadaeafb0', - 'ecb-tbl-192: I=23'), - ('40be8c5d9108e663f38f1a2395279ecf', 'c9c82766176a9b228eb9a974a010b4fb', - 'd0d1d2d3d5d6d7d8dadbdcdddfe0e1e2e4e5e6e7e9eaebec', - 'ecb-tbl-192: I=24'), - ('0c8ad9bc32d43e04716753aa4cfbe351', 'd8e26aa02945881d5137f1c1e1386e88', - '2a2b2c2d2f30313234353637393a3b3c3e3f404143444546', - 'ecb-tbl-192: I=25'), - ('1407b1d5f87d63357c8dc7ebbaebbfee', 'c0e024ccd68ff5ffa4d139c355a77c55', - '48494a4b4d4e4f50525354555758595a5c5d5e5f61626364', - 'ecb-tbl-192: I=26'), - ('e62734d1ae3378c4549e939e6f123416', '0b18b3d16f491619da338640df391d43', - '84858687898a8b8c8e8f90919394959698999a9b9d9e9fa0', - 'ecb-tbl-192: I=27'), - ('5a752cff2a176db1a1de77f2d2cdee41', 'dbe09ac8f66027bf20cb6e434f252efc', - 'a2a3a4a5a7a8a9aaacadaeafb1b2b3b4b6b7b8b9bbbcbdbe', - 'ecb-tbl-192: I=28'), - ('a9c8c3a4eabedc80c64730ddd018cd88', '6d04e5e43c5b9cbe05feb9606b6480fe', - 'c0c1c2c3c5c6c7c8cacbcccdcfd0d1d2d4d5d6d7d9dadbdc', - 'ecb-tbl-192: I=29'), - ('ee9b3dbbdb86180072130834d305999a', 'dd1d6553b96be526d9fee0fbd7176866', - '1a1b1c1d1f20212224252627292a2b2c2e2f303133343536', - 'ecb-tbl-192: I=30'), - ('a7fa8c3586b8ebde7568ead6f634a879', '0260ca7e3f979fd015b0dd4690e16d2a', - '38393a3b3d3e3f40424344454748494a4c4d4e4f51525354', - 'ecb-tbl-192: I=31'), - ('37e0f4a87f127d45ac936fe7ad88c10a', '9893734de10edcc8a67c3b110b8b8cc6', - '929394959798999a9c9d9e9fa1a2a3a4a6a7a8a9abacadae', - 'ecb-tbl-192: I=32'), - ('3f77d8b5d92bac148e4e46f697a535c5', '93b30b750516b2d18808d710c2ee84ef', - '464748494b4c4d4e50515253555657585a5b5c5d5f606162', - 'ecb-tbl-192: I=33'), - ('d25ebb686c40f7e2c4da1014936571ca', '16f65fa47be3cb5e6dfe7c6c37016c0e', - '828384858788898a8c8d8e8f91929394969798999b9c9d9e', - 'ecb-tbl-192: I=34'), - ('4f1c769d1e5b0552c7eca84dea26a549', 'f3847210d5391e2360608e5acb560581', - 'a0a1a2a3a5a6a7a8aaabacadafb0b1b2b4b5b6b7b9babbbc', - 'ecb-tbl-192: I=35'), - ('8548e2f882d7584d0fafc54372b6633a', '8754462cd223366d0753913e6af2643d', - 'bebfc0c1c3c4c5c6c8c9cacbcdcecfd0d2d3d4d5d7d8d9da', - 'ecb-tbl-192: I=36'), - ('87d7a336cb476f177cd2a51af2a62cdf', '1ea20617468d1b806a1fd58145462017', - 'dcdddedfe1e2e3e4e6e7e8e9ebecedeef0f1f2f3f5f6f7f8', - 'ecb-tbl-192: I=37'), - ('03b1feac668c4e485c1065dfc22b44ee', '3b155d927355d737c6be9dda60136e2e', - 'fafbfcfdfe01000204050607090a0b0c0e0f101113141516', - 'ecb-tbl-192: I=38'), - ('bda15e66819fa72d653a6866aa287962', '26144f7b66daa91b6333dbd3850502b3', - '18191a1b1d1e1f20222324252728292a2c2d2e2f31323334', - 'ecb-tbl-192: I=39'), - ('4d0c7a0d2505b80bf8b62ceb12467f0a', 'e4f9a4ab52ced8134c649bf319ebcc90', - '363738393b3c3d3e40414243454647484a4b4c4d4f505152', - 'ecb-tbl-192: I=40'), - ('626d34c9429b37211330986466b94e5f', 'b9ddd29ac6128a6cab121e34a4c62b36', - '54555657595a5b5c5e5f60616364656668696a6b6d6e6f70', - 'ecb-tbl-192: I=41'), - ('333c3e6bf00656b088a17e5ff0e7f60a', '6fcddad898f2ce4eff51294f5eaaf5c9', - '727374757778797a7c7d7e7f81828384868788898b8c8d8e', - 'ecb-tbl-192: I=42'), - ('687ed0cdc0d2a2bc8c466d05ef9d2891', 'c9a6fe2bf4028080bea6f7fc417bd7e3', - '90919293959697989a9b9c9d9fa0a1a2a4a5a6a7a9aaabac', - 'ecb-tbl-192: I=43'), - ('487830e78cc56c1693e64b2a6660c7b6', '6a2026846d8609d60f298a9c0673127f', - 'aeafb0b1b3b4b5b6b8b9babbbdbebfc0c2c3c4c5c7c8c9ca', - 'ecb-tbl-192: I=44'), - ('7a48d6b7b52b29392aa2072a32b66160', '2cb25c005e26efea44336c4c97a4240b', - 'cccdcecfd1d2d3d4d6d7d8d9dbdcdddee0e1e2e3e5e6e7e8', - 'ecb-tbl-192: I=45'), - ('907320e64c8c5314d10f8d7a11c8618d', '496967ab8680ddd73d09a0e4c7dcc8aa', - 'eaebecedeff0f1f2f4f5f6f7f9fafbfcfefe010103040506', - 'ecb-tbl-192: I=46'), - ('b561f2ca2d6e65a4a98341f3ed9ff533', 'd5af94de93487d1f3a8c577cb84a66a4', - '08090a0b0d0e0f10121314151718191a1c1d1e1f21222324', - 'ecb-tbl-192: I=47'), - ('df769380d212792d026f049e2e3e48ef', '84bdac569cae2828705f267cc8376e90', - '262728292b2c2d2e30313233353637383a3b3c3d3f404142', - 'ecb-tbl-192: I=48'), - ('79f374bc445bdabf8fccb8843d6054c6', 'f7401dda5ad5ab712b7eb5d10c6f99b6', - '44454647494a4b4c4e4f50515354555658595a5b5d5e5f60', - 'ecb-tbl-192: I=49'), - ('4e02f1242fa56b05c68dbae8fe44c9d6', '1c9d54318539ebd4c3b5b7e37bf119f0', - '626364656768696a6c6d6e6f71727374767778797b7c7d7e', - 'ecb-tbl-192: I=50'), - ('cf73c93cbff57ac635a6f4ad2a4a1545', 'aca572d65fb2764cffd4a6eca090ea0d', - '80818283858687888a8b8c8d8f90919294959697999a9b9c', - 'ecb-tbl-192: I=51'), - ('9923548e2875750725b886566784c625', '36d9c627b8c2a886a10ccb36eae3dfbb', - '9e9fa0a1a3a4a5a6a8a9aaabadaeafb0b2b3b4b5b7b8b9ba', - 'ecb-tbl-192: I=52'), - ('4888336b723a022c9545320f836a4207', '010edbf5981e143a81d646e597a4a568', - 'bcbdbebfc1c2c3c4c6c7c8c9cbcccdced0d1d2d3d5d6d7d8', - 'ecb-tbl-192: I=53'), - ('f84d9a5561b0608b1160dee000c41ba8', '8db44d538dc20cc2f40f3067fd298e60', - 'dadbdcdddfe0e1e2e4e5e6e7e9eaebeceeeff0f1f3f4f5f6', - 'ecb-tbl-192: I=54'), - ('c23192a0418e30a19b45ae3e3625bf22', '930eb53bc71e6ac4b82972bdcd5aafb3', - 'f8f9fafbfdfefe00020304050708090a0c0d0e0f11121314', - 'ecb-tbl-192: I=55'), - ('b84e0690b28b0025381ad82a15e501a7', '6c42a81edcbc9517ccd89c30c95597b4', - '161718191b1c1d1e20212223252627282a2b2c2d2f303132', - 'ecb-tbl-192: I=56'), - ('acef5e5c108876c4f06269f865b8f0b0', 'da389847ad06df19d76ee119c71e1dd3', - '34353637393a3b3c3e3f40414344454648494a4b4d4e4f50', - 'ecb-tbl-192: I=57'), - ('0f1b3603e0f5ddea4548246153a5e064', 'e018fdae13d3118f9a5d1a647a3f0462', - '525354555758595a5c5d5e5f61626364666768696b6c6d6e', - 'ecb-tbl-192: I=58'), - ('fbb63893450d42b58c6d88cd3c1809e3', '2aa65db36264239d3846180fabdfad20', - '70717273757677787a7b7c7d7f80818284858687898a8b8c', - 'ecb-tbl-192: I=59'), - ('4bef736df150259dae0c91354e8a5f92', '1472163e9a4f780f1ceb44b07ecf4fdb', - '8e8f90919394959698999a9b9d9e9fa0a2a3a4a5a7a8a9aa', - 'ecb-tbl-192: I=60'), - ('7d2d46242056ef13d3c3fc93c128f4c7', 'c8273fdc8f3a9f72e91097614b62397c', - 'acadaeafb1b2b3b4b6b7b8b9bbbcbdbec0c1c2c3c5c6c7c8', - 'ecb-tbl-192: I=61'), - ('e9c1ba2df415657a256edb33934680fd', '66c8427dcd733aaf7b3470cb7d976e3f', - 'cacbcccdcfd0d1d2d4d5d6d7d9dadbdcdedfe0e1e3e4e5e6', - 'ecb-tbl-192: I=62'), - ('e23ee277b0aa0a1dfb81f7527c3514f1', '146131cb17f1424d4f8da91e6f80c1d0', - 'e8e9eaebedeeeff0f2f3f4f5f7f8f9fafcfdfeff01020304', - 'ecb-tbl-192: I=63'), - ('3e7445b0b63caaf75e4a911e12106b4c', '2610d0ad83659081ae085266a88770dc', - '060708090b0c0d0e10111213151617181a1b1c1d1f202122', - 'ecb-tbl-192: I=64'), - ('767774752023222544455a5be6e1e0e3', '38a2b5a974b0575c5d733917fb0d4570', - '24252627292a2b2c2e2f30313334353638393a3b3d3e3f40', - 'ecb-tbl-192: I=65'), - ('72737475717e7f7ce9e8ebea696a6b6c', 'e21d401ebc60de20d6c486e4f39a588b', - '424344454748494a4c4d4e4f51525354565758595b5c5d5e', - 'ecb-tbl-192: I=66'), - ('dfdedddc25262728c9c8cfcef1eeefec', 'e51d5f88c670b079c0ca1f0c2c4405a2', - '60616263656667686a6b6c6d6f70717274757677797a7b7c', - 'ecb-tbl-192: I=67'), - ('fffe0100707776755f5e5d5c7675746b', '246a94788a642fb3d1b823c8762380c8', - '7e7f80818384858688898a8b8d8e8f90929394959798999a', - 'ecb-tbl-192: I=68'), - ('e0e1e2e3424140479f9e9190292e2f2c', 'b80c391c5c41a4c3b30c68e0e3d7550f', - '9c9d9e9fa1a2a3a4a6a7a8a9abacadaeb0b1b2b3b5b6b7b8', - 'ecb-tbl-192: I=69'), - ('2120272690efeeed3b3a39384e4d4c4b', 'b77c4754fc64eb9a1154a9af0bb1f21c', - 'babbbcbdbfc0c1c2c4c5c6c7c9cacbcccecfd0d1d3d4d5d6', - 'ecb-tbl-192: I=70'), - ('ecedeeef5350516ea1a0a7a6a3acadae', 'fb554de520d159a06bf219fc7f34a02f', - 'd8d9dadbdddedfe0e2e3e4e5e7e8e9eaecedeeeff1f2f3f4', - 'ecb-tbl-192: I=71'), - ('32333c3d25222320e9e8ebeacecdccc3', 'a89fba152d76b4927beed160ddb76c57', - 'f6f7f8f9fbfcfdfe00010203050607080a0b0c0d0f101112', - 'ecb-tbl-192: I=72'), - ('40414243626160678a8bb4b511161714', '5676eab4a98d2e8473b3f3d46424247c', - '14151617191a1b1c1e1f20212324252628292a2b2d2e2f30', - 'ecb-tbl-192: I=73'), - ('94959293f5fafbf81f1e1d1c7c7f7e79', '4e8f068bd7ede52a639036ec86c33568', - '323334353738393a3c3d3e3f41424344464748494b4c4d4e', - 'ecb-tbl-192: I=74'), - ('bebfbcbd191a1b14cfcec9c8546b6a69', 'f0193c4d7aff1791ee4c07eb4a1824fc', - '50515253555657585a5b5c5d5f60616264656667696a6b6c', - 'ecb-tbl-192: I=75'), - ('2c2d3233898e8f8cbbbab9b8333031ce', 'ac8686eeca9ba761afe82d67b928c33f', - '6e6f70717374757678797a7b7d7e7f80828384858788898a', - 'ecb-tbl-192: I=76'), - ('84858687bfbcbdba37363938fdfafbf8', '5faf8573e33b145b6a369cd3606ab2c9', - '8c8d8e8f91929394969798999b9c9d9ea0a1a2a3a5a6a7a8', - 'ecb-tbl-192: I=77'), - ('828384857669686b909192930b08090e', '31587e9944ab1c16b844ecad0df2e7da', - 'aaabacadafb0b1b2b4b5b6b7b9babbbcbebfc0c1c3c4c5c6', - 'ecb-tbl-192: I=78'), - ('bebfbcbd9695948b707176779e919093', 'd017fecd91148aba37f6f3068aa67d8a', - 'c8c9cacbcdcecfd0d2d3d4d5d7d8d9dadcdddedfe1e2e3e4', - 'ecb-tbl-192: I=79'), - ('8b8a85846067666521202322d0d3d2dd', '788ef2f021a73cba2794b616078a8500', - 'e6e7e8e9ebecedeef0f1f2f3f5f6f7f8fafbfcfdfe010002', - 'ecb-tbl-192: I=80'), - ('76777475f1f2f3f4f8f9e6e777707172', '5d1ef20dced6bcbc12131ac7c54788aa', - '04050607090a0b0c0e0f10111314151618191a1b1d1e1f20', - 'ecb-tbl-192: I=81'), - ('a4a5a2a34f404142b4b5b6b727242522', 'b3c8cf961faf9ea05fdde6d1e4d8f663', - '222324252728292a2c2d2e2f31323334363738393b3c3d3e', - 'ecb-tbl-192: I=82'), - ('94959697e1e2e3ec16171011839c9d9e', '143075c70605861c7fac6526199e459f', - '40414243454647484a4b4c4d4f50515254555657595a5b5c', - 'ecb-tbl-192: I=83'), - ('03023d3c06010003dedfdcddfffcfde2', 'a5ae12eade9a87268d898bfc8fc0252a', - '5e5f60616364656668696a6b6d6e6f70727374757778797a', - 'ecb-tbl-192: I=84'), - ('10111213f1f2f3f4cecfc0c1dbdcddde', '0924f7cf2e877a4819f5244a360dcea9', - '7c7d7e7f81828384868788898b8c8d8e9091929395969798', - 'ecb-tbl-192: I=85'), - ('67666160724d4c4f1d1c1f1e73707176', '3d9e9635afcc3e291cc7ab3f27d1c99a', - '9a9b9c9d9fa0a1a2a4a5a6a7a9aaabacaeafb0b1b3b4b5b6', - 'ecb-tbl-192: I=86'), - ('e6e7e4e5a8abaad584858283909f9e9d', '9d80feebf87510e2b8fb98bb54fd788c', - 'b8b9babbbdbebfc0c2c3c4c5c7c8c9cacccdcecfd1d2d3d4', - 'ecb-tbl-192: I=87'), - ('71707f7e565150537d7c7f7e6162636c', '5f9d1a082a1a37985f174002eca01309', - 'd6d7d8d9dbdcdddee0e1e2e3e5e6e7e8eaebecedeff0f1f2', - 'ecb-tbl-192: I=88'), - ('64656667212223245555aaaa03040506', 'a390ebb1d1403930184a44b4876646e4', - 'f4f5f6f7f9fafbfcfefe01010304050608090a0b0d0e0f10', - 'ecb-tbl-192: I=89'), - ('9e9f9899aba4a5a6cfcecdcc2b28292e', '700fe918981c3195bb6c4bcb46b74e29', - '121314151718191a1c1d1e1f21222324262728292b2c2d2e', - 'ecb-tbl-192: I=90'), - ('c7c6c5c4d1d2d3dc626364653a454447', '907984406f7bf2d17fb1eb15b673d747', - '30313233353637383a3b3c3d3f40414244454647494a4b4c', - 'ecb-tbl-192: I=91'), - ('f6f7e8e9e0e7e6e51d1c1f1e5b585966', 'c32a956dcfc875c2ac7c7cc8b8cc26e1', - '4e4f50515354555658595a5b5d5e5f60626364656768696a', - 'ecb-tbl-192: I=92'), - ('bcbdbebf5d5e5f5868696667f4f3f2f1', '02646e2ebfa9b820cf8424e9b9b6eb51', - '6c6d6e6f71727374767778797b7c7d7e8081828385868788', - 'ecb-tbl-192: I=93'), - ('40414647b0afaead9b9a99989b98999e', '621fda3a5bbd54c6d3c685816bd4ead8', - '8a8b8c8d8f90919294959697999a9b9c9e9fa0a1a3a4a5a6', - 'ecb-tbl-192: I=94'), - ('69686b6a0201001f0f0e0908b4bbbab9', 'd4e216040426dfaf18b152469bc5ac2f', - 'a8a9aaabadaeafb0b2b3b4b5b7b8b9babcbdbebfc1c2c3c4', - 'ecb-tbl-192: I=95'), - ('c7c6c9c8d8dfdedd5a5b5859bebdbcb3', '9d0635b9d33b6cdbd71f5d246ea17cc8', - 'c6c7c8c9cbcccdced0d1d2d3d5d6d7d8dadbdcdddfe0e1e2', - 'ecb-tbl-192: I=96'), - ('dedfdcdd787b7a7dfffee1e0b2b5b4b7', '10abad1bd9bae5448808765583a2cc1a', - 'e4e5e6e7e9eaebeceeeff0f1f3f4f5f6f8f9fafbfdfefe00', - 'ecb-tbl-192: I=97'), - ('4d4c4b4a606f6e6dd0d1d2d3fbf8f9fe', '6891889e16544e355ff65a793c39c9a8', - '020304050708090a0c0d0e0f11121314161718191b1c1d1e', - 'ecb-tbl-192: I=98'), - ('b7b6b5b4d7d4d5dae5e4e3e2e1fefffc', 'cc735582e68072c163cd9ddf46b91279', - '20212223252627282a2b2c2d2f30313234353637393a3b3c', - 'ecb-tbl-192: I=99'), - ('cecfb0b1f7f0f1f2aeafacad3e3d3c23', 'c5c68b9aeeb7f878df578efa562f9574', - '3e3f40414344454648494a4b4d4e4f50525354555758595a', - 'ecb-tbl-192: I=100'), - ('cacbc8c9cdcecfc812131c1d494e4f4c', '5f4764395a667a47d73452955d0d2ce8', - '5c5d5e5f61626364666768696b6c6d6e7071727375767778', - 'ecb-tbl-192: I=101'), - ('9d9c9b9ad22d2c2fb1b0b3b20c0f0e09', '701448331f66106cefddf1eb8267c357', - '7a7b7c7d7f80818284858687898a8b8c8e8f909193949596', - 'ecb-tbl-192: I=102'), - ('7a7b787964676659959493924f404142', 'cb3ee56d2e14b4e1941666f13379d657', - '98999a9b9d9e9fa0a2a3a4a5a7a8a9aaacadaeafb1b2b3b4', - 'ecb-tbl-192: I=103'), - ('aaaba4a5cec9c8cb1f1e1d1caba8a9a6', '9fe16efd18ab6e1981191851fedb0764', - 'b6b7b8b9bbbcbdbec0c1c2c3c5c6c7c8cacbcccdcfd0d1d2', - 'ecb-tbl-192: I=104'), - ('93929190282b2a2dc4c5fafb92959497', '3dc9ba24e1b223589b147adceb4c8e48', - 'd4d5d6d7d9dadbdcdedfe0e1e3e4e5e6e8e9eaebedeeeff0', - 'ecb-tbl-192: I=105'), - ('efeee9e8ded1d0d339383b3a888b8a8d', '1c333032682e7d4de5e5afc05c3e483c', - 'f2f3f4f5f7f8f9fafcfdfeff01020304060708090b0c0d0e', - 'ecb-tbl-192: I=106'), - ('7f7e7d7ca2a1a0af78797e7f112e2f2c', 'd593cc99a95afef7e92038e05a59d00a', - '10111213151617181a1b1c1d1f20212224252627292a2b2c', - 'ecb-tbl-192: I=107'), - ('84859a9b2b2c2d2e868784852625245b', '51e7f96f53b4353923452c222134e1ec', - '2e2f30313334353638393a3b3d3e3f40424344454748494a', - 'ecb-tbl-192: I=108'), - ('b0b1b2b3070405026869666710171615', '4075b357a1a2b473400c3b25f32f81a4', - '4c4d4e4f51525354565758595b5c5d5e6061626365666768', - 'ecb-tbl-192: I=109'), - ('acadaaabbda2a3a00d0c0f0e595a5b5c', '302e341a3ebcd74f0d55f61714570284', - '6a6b6c6d6f70717274757677797a7b7c7e7f808183848586', - 'ecb-tbl-192: I=110'), - ('121310115655544b5253545569666764', '57abdd8231280da01c5042b78cf76522', - '88898a8b8d8e8f90929394959798999a9c9d9e9fa1a2a3a4', - 'ecb-tbl-192: I=111'), - ('dedfd0d166616063eaebe8e94142434c', '17f9ea7eea17ac1adf0e190fef799e92', - 'a6a7a8a9abacadaeb0b1b2b3b5b6b7b8babbbcbdbfc0c1c2', - 'ecb-tbl-192: I=112'), - ('dbdad9d81417161166677879e0e7e6e5', '2e1bdd563dd87ee5c338dd6d098d0a7a', - 'c4c5c6c7c9cacbcccecfd0d1d3d4d5d6d8d9dadbdddedfe0', - 'ecb-tbl-192: I=113'), - ('6a6b6c6de0efeeed2b2a2928c0c3c2c5', 'eb869996e6f8bfb2bfdd9e0c4504dbb2', - 'e2e3e4e5e7e8e9eaecedeeeff1f2f3f4f6f7f8f9fbfcfdfe', - 'ecb-tbl-192: I=114'), - ('b1b0b3b21714151a1a1b1c1d5649484b', 'c2e01549e9decf317468b3e018c61ba8', - '00010203050607080a0b0c0d0f10111214151617191a1b1c', - 'ecb-tbl-192: I=115'), - ('39380706a3a4a5a6c4c5c6c77271706f', '8da875d033c01dd463b244a1770f4a22', - '1e1f20212324252628292a2b2d2e2f30323334353738393a', - 'ecb-tbl-192: I=116'), - ('5c5d5e5f1013121539383736e2e5e4e7', '8ba0dcf3a186844f026d022f8839d696', - '3c3d3e3f41424344464748494b4c4d4e5051525355565758', - 'ecb-tbl-192: I=117'), - ('43424544ead5d4d72e2f2c2d64676661', 'e9691ff9a6cc6970e51670a0fd5b88c1', - '5a5b5c5d5f60616264656667696a6b6c6e6f707173747576', - 'ecb-tbl-192: I=118'), - ('55545756989b9a65f8f9feff18171615', 'f2baec06faeed30f88ee63ba081a6e5b', - '78797a7b7d7e7f80828384858788898a8c8d8e8f91929394', - 'ecb-tbl-192: I=119'), - ('05040b0a525554573c3d3e3f4a494847', '9c39d4c459ae5753394d6094adc21e78', - '969798999b9c9d9ea0a1a2a3a5a6a7a8aaabacadafb0b1b2', - 'ecb-tbl-192: I=120'), - ('14151617595a5b5c8584fbfa8e89888b', '6345b532a11904502ea43ba99c6bd2b2', - 'b4b5b6b7b9babbbcbebfc0c1c3c4c5c6c8c9cacbcdcecfd0', - 'ecb-tbl-192: I=121'), - ('7c7d7a7bfdf2f3f029282b2a51525354', '5ffae3061a95172e4070cedce1e428c8', - 'd2d3d4d5d7d8d9dadcdddedfe1e2e3e4e6e7e8e9ebecedee', - 'ecb-tbl-192: I=122'), - ('38393a3b1e1d1c1341404746c23d3c3e', '0a4566be4cdf9adce5dec865b5ab34cd', - 'f0f1f2f3f5f6f7f8fafbfcfdfe01000204050607090a0b0c', - 'ecb-tbl-192: I=123'), - ('8d8c939240474645818083827c7f7e41', 'ca17fcce79b7404f2559b22928f126fb', - '0e0f10111314151618191a1b1d1e1f20222324252728292a', - 'ecb-tbl-192: I=124'), - ('3b3a39381a19181f32333c3d45424340', '97ca39b849ed73a6470a97c821d82f58', - '2c2d2e2f31323334363738393b3c3d3e4041424345464748', - 'ecb-tbl-192: I=125'), - ('f0f1f6f738272625828380817f7c7d7a', '8198cb06bc684c6d3e9b7989428dcf7a', - '4a4b4c4d4f50515254555657595a5b5c5e5f606163646566', - 'ecb-tbl-192: I=126'), - ('89888b8a0407061966676061141b1a19', 'f53c464c705ee0f28d9a4c59374928bd', - '68696a6b6d6e6f70727374757778797a7c7d7e7f81828384', - 'ecb-tbl-192: I=127'), - ('d3d2dddcaaadacaf9c9d9e9fe8ebeae5', '9adb3d4cca559bb98c3e2ed73dbf1154', - '868788898b8c8d8e90919293959697989a9b9c9d9fa0a1a2', - 'ecb-tbl-192: I=128'), - - # ecb_tbl.txt, KEYSIZE=256 - ('834eadfccac7e1b30664b1aba44815ab', '1946dabf6a03a2a2c3d0b05080aed6fc', - '00010203050607080a0b0c0d0f10111214151617191a1b1c1e1f202123242526', - 'ecb-tbl-256: I=1'), - ('d9dc4dba3021b05d67c0518f72b62bf1', '5ed301d747d3cc715445ebdec62f2fb4', - '28292a2b2d2e2f30323334353738393a3c3d3e3f41424344464748494b4c4d4e', - 'ecb-tbl-256: I=2'), - ('a291d86301a4a739f7392173aa3c604c', '6585c8f43d13a6beab6419fc5935b9d0', - '50515253555657585a5b5c5d5f60616264656667696a6b6c6e6f707173747576', - 'ecb-tbl-256: I=3'), - ('4264b2696498de4df79788a9f83e9390', '2a5b56a596680fcc0e05f5e0f151ecae', - '78797a7b7d7e7f80828384858788898a8c8d8e8f91929394969798999b9c9d9e', - 'ecb-tbl-256: I=4'), - ('ee9932b3721804d5a83ef5949245b6f6', 'f5d6ff414fd2c6181494d20c37f2b8c4', - 'a0a1a2a3a5a6a7a8aaabacadafb0b1b2b4b5b6b7b9babbbcbebfc0c1c3c4c5c6', - 'ecb-tbl-256: I=5'), - ('e6248f55c5fdcbca9cbbb01c88a2ea77', '85399c01f59fffb5204f19f8482f00b8', - 'c8c9cacbcdcecfd0d2d3d4d5d7d8d9dadcdddedfe1e2e3e4e6e7e8e9ebecedee', - 'ecb-tbl-256: I=6'), - ('b8358e41b9dff65fd461d55a99266247', '92097b4c88a041ddf98144bc8d22e8e7', - 'f0f1f2f3f5f6f7f8fafbfcfdfe01000204050607090a0b0c0e0f101113141516', - 'ecb-tbl-256: I=7'), - ('f0e2d72260af58e21e015ab3a4c0d906', '89bd5b73b356ab412aef9f76cea2d65c', - '18191a1b1d1e1f20222324252728292a2c2d2e2f31323334363738393b3c3d3e', - 'ecb-tbl-256: I=8'), - ('475b8b823ce8893db3c44a9f2a379ff7', '2536969093c55ff9454692f2fac2f530', - '40414243454647484a4b4c4d4f50515254555657595a5b5c5e5f606163646566', - 'ecb-tbl-256: I=9'), - ('688f5281945812862f5f3076cf80412f', '07fc76a872843f3f6e0081ee9396d637', - '68696a6b6d6e6f70727374757778797a7c7d7e7f81828384868788898b8c8d8e', - 'ecb-tbl-256: I=10'), - ('08d1d2bc750af553365d35e75afaceaa', 'e38ba8ec2aa741358dcc93e8f141c491', - '90919293959697989a9b9c9d9fa0a1a2a4a5a6a7a9aaabacaeafb0b1b3b4b5b6', - 'ecb-tbl-256: I=11'), - ('8707121f47cc3efceca5f9a8474950a1', 'd028ee23e4a89075d0b03e868d7d3a42', - 'b8b9babbbdbebfc0c2c3c4c5c7c8c9cacccdcecfd1d2d3d4d6d7d8d9dbdcddde', - 'ecb-tbl-256: I=12'), - ('e51aa0b135dba566939c3b6359a980c5', '8cd9423dfc459e547155c5d1d522e540', - 'e0e1e2e3e5e6e7e8eaebecedeff0f1f2f4f5f6f7f9fafbfcfefe010103040506', - 'ecb-tbl-256: I=13'), - ('069a007fc76a459f98baf917fedf9521', '080e9517eb1677719acf728086040ae3', - '08090a0b0d0e0f10121314151718191a1c1d1e1f21222324262728292b2c2d2e', - 'ecb-tbl-256: I=14'), - ('726165c1723fbcf6c026d7d00b091027', '7c1700211a3991fc0ecded0ab3e576b0', - '30313233353637383a3b3c3d3f40414244454647494a4b4c4e4f505153545556', - 'ecb-tbl-256: I=15'), - ('d7c544de91d55cfcde1f84ca382200ce', 'dabcbcc855839251db51e224fbe87435', - '58595a5b5d5e5f60626364656768696a6c6d6e6f71727374767778797b7c7d7e', - 'ecb-tbl-256: I=16'), - ('fed3c9a161b9b5b2bd611b41dc9da357', '68d56fad0406947a4dd27a7448c10f1d', - '80818283858687888a8b8c8d8f90919294959697999a9b9c9e9fa0a1a3a4a5a6', - 'ecb-tbl-256: I=17'), - ('4f634cdc6551043409f30b635832cf82', 'da9a11479844d1ffee24bbf3719a9925', - 'a8a9aaabadaeafb0b2b3b4b5b7b8b9babcbdbebfc1c2c3c4c6c7c8c9cbcccdce', - 'ecb-tbl-256: I=18'), - ('109ce98db0dfb36734d9f3394711b4e6', '5e4ba572f8d23e738da9b05ba24b8d81', - 'd0d1d2d3d5d6d7d8dadbdcdddfe0e1e2e4e5e6e7e9eaebeceeeff0f1f3f4f5f6', - 'ecb-tbl-256: I=19'), - ('4ea6dfaba2d8a02ffdffa89835987242', 'a115a2065d667e3f0b883837a6e903f8', - '70717273757677787a7b7c7d7f80818284858687898a8b8c8e8f909193949596', - 'ecb-tbl-256: I=20'), - ('5ae094f54af58e6e3cdbf976dac6d9ef', '3e9e90dc33eac2437d86ad30b137e66e', - '98999a9b9d9e9fa0a2a3a4a5a7a8a9aaacadaeafb1b2b3b4b6b7b8b9bbbcbdbe', - 'ecb-tbl-256: I=21'), - ('764d8e8e0f29926dbe5122e66354fdbe', '01ce82d8fbcdae824cb3c48e495c3692', - 'c0c1c2c3c5c6c7c8cacbcccdcfd0d1d2d4d5d6d7d9dadbdcdedfe0e1e3e4e5e6', - 'ecb-tbl-256: I=22'), - ('3f0418f888cdf29a982bf6b75410d6a9', '0c9cff163ce936faaf083cfd3dea3117', - 'e8e9eaebedeeeff0f2f3f4f5f7f8f9fafcfdfeff01020304060708090b0c0d0e', - 'ecb-tbl-256: I=23'), - ('e4a3e7cb12cdd56aa4a75197a9530220', '5131ba9bd48f2bba85560680df504b52', - '10111213151617181a1b1c1d1f20212224252627292a2b2c2e2f303133343536', - 'ecb-tbl-256: I=24'), - ('211677684aac1ec1a160f44c4ebf3f26', '9dc503bbf09823aec8a977a5ad26ccb2', - '38393a3b3d3e3f40424344454748494a4c4d4e4f51525354565758595b5c5d5e', - 'ecb-tbl-256: I=25'), - ('d21e439ff749ac8f18d6d4b105e03895', '9a6db0c0862e506a9e397225884041d7', - '60616263656667686a6b6c6d6f70717274757677797a7b7c7e7f808183848586', - 'ecb-tbl-256: I=26'), - ('d9f6ff44646c4725bd4c0103ff5552a7', '430bf9570804185e1ab6365fc6a6860c', - '88898a8b8d8e8f90929394959798999a9c9d9e9fa1a2a3a4a6a7a8a9abacadae', - 'ecb-tbl-256: I=27'), - ('0b1256c2a00b976250cfc5b0c37ed382', '3525ebc02f4886e6a5a3762813e8ce8a', - 'b0b1b2b3b5b6b7b8babbbcbdbfc0c1c2c4c5c6c7c9cacbcccecfd0d1d3d4d5d6', - 'ecb-tbl-256: I=28'), - ('b056447ffc6dc4523a36cc2e972a3a79', '07fa265c763779cce224c7bad671027b', - 'd8d9dadbdddedfe0e2e3e4e5e7e8e9eaecedeeeff1f2f3f4f6f7f8f9fbfcfdfe', - 'ecb-tbl-256: I=29'), - ('5e25ca78f0de55802524d38da3fe4456', 'e8b72b4e8be243438c9fff1f0e205872', - '00010203050607080a0b0c0d0f10111214151617191a1b1c1e1f202123242526', - 'ecb-tbl-256: I=30'), - ('a5bcf4728fa5eaad8567c0dc24675f83', '109d4f999a0e11ace1f05e6b22cbcb50', - '28292a2b2d2e2f30323334353738393a3c3d3e3f41424344464748494b4c4d4e', - 'ecb-tbl-256: I=31'), - ('814e59f97ed84646b78b2ca022e9ca43', '45a5e8d4c3ed58403ff08d68a0cc4029', - '50515253555657585a5b5c5d5f60616264656667696a6b6c6e6f707173747576', - 'ecb-tbl-256: I=32'), - ('15478beec58f4775c7a7f5d4395514d7', '196865964db3d417b6bd4d586bcb7634', - '78797a7b7d7e7f80828384858788898a8c8d8e8f91929394969798999b9c9d9e', - 'ecb-tbl-256: I=33'), - ('253548ffca461c67c8cbc78cd59f4756', '60436ad45ac7d30d99195f815d98d2ae', - 'a0a1a2a3a5a6a7a8aaabacadafb0b1b2b4b5b6b7b9babbbcbebfc0c1c3c4c5c6', - 'ecb-tbl-256: I=34'), - ('fd7ad8d73b9b0f8cc41600640f503d65', 'bb07a23f0b61014b197620c185e2cd75', - 'c8c9cacbcdcecfd0d2d3d4d5d7d8d9dadcdddedfe1e2e3e4e6e7e8e9ebecedee', - 'ecb-tbl-256: I=35'), - ('06199de52c6cbf8af954cd65830bcd56', '5bc0b2850129c854423aff0751fe343b', - 'f0f1f2f3f5f6f7f8fafbfcfdfe01000204050607090a0b0c0e0f101113141516', - 'ecb-tbl-256: I=36'), - ('f17c4ffe48e44c61bd891e257e725794', '7541a78f96738e6417d2a24bd2beca40', - '18191a1b1d1e1f20222324252728292a2c2d2e2f31323334363738393b3c3d3e', - 'ecb-tbl-256: I=37'), - ('9a5b4a402a3e8a59be6bf5cd8154f029', 'b0a303054412882e464591f1546c5b9e', - '40414243454647484a4b4c4d4f50515254555657595a5b5c5e5f606163646566', - 'ecb-tbl-256: I=38'), - ('79bd40b91a7e07dc939d441782ae6b17', '778c06d8a355eeee214fcea14b4e0eef', - '68696a6b6d6e6f70727374757778797a7c7d7e7f81828384868788898b8c8d8e', - 'ecb-tbl-256: I=39'), - ('d8ceaaf8976e5fbe1012d8c84f323799', '09614206d15cbace63227d06db6beebb', - '90919293959697989a9b9c9d9fa0a1a2a4a5a6a7a9aaabacaeafb0b1b3b4b5b6', - 'ecb-tbl-256: I=40'), - ('3316e2751e2e388b083da23dd6ac3fbe', '41b97fb20e427a9fdbbb358d9262255d', - 'b8b9babbbdbebfc0c2c3c4c5c7c8c9cacccdcecfd1d2d3d4d6d7d8d9dbdcddde', - 'ecb-tbl-256: I=41'), - ('8b7cfbe37de7dca793521819242c5816', 'c1940f703d845f957652c2d64abd7adf', - 'e0e1e2e3e5e6e7e8eaebecedeff0f1f2f4f5f6f7f9fafbfcfefe010103040506', - 'ecb-tbl-256: I=42'), - ('f23f033c0eebf8ec55752662fd58ce68', 'd2d44fcdae5332343366db297efcf21b', - '08090a0b0d0e0f10121314151718191a1c1d1e1f21222324262728292b2c2d2e', - 'ecb-tbl-256: I=43'), - ('59eb34f6c8bdbacc5fc6ad73a59a1301', 'ea8196b79dbe167b6aa9896e287eed2b', - '30313233353637383a3b3c3d3f40414244454647494a4b4c4e4f505153545556', - 'ecb-tbl-256: I=44'), - ('dcde8b6bd5cf7cc22d9505e3ce81261a', 'd6b0b0c4ba6c7dbe5ed467a1e3f06c2d', - '58595a5b5d5e5f60626364656768696a6c6d6e6f71727374767778797b7c7d7e', - 'ecb-tbl-256: I=45'), - ('e33cf7e524fed781e7042ff9f4b35dc7', 'ec51eb295250c22c2fb01816fb72bcae', - '80818283858687888a8b8c8d8f90919294959697999a9b9c9e9fa0a1a3a4a5a6', - 'ecb-tbl-256: I=46'), - ('27963c8facdf73062867d164df6d064c', 'aded6630a07ce9c7408a155d3bd0d36f', - 'a8a9aaabadaeafb0b2b3b4b5b7b8b9babcbdbebfc1c2c3c4c6c7c8c9cbcccdce', - 'ecb-tbl-256: I=47'), - ('77b1ce386b551b995f2f2a1da994eef8', '697c9245b9937f32f5d1c82319f0363a', - 'd0d1d2d3d5d6d7d8dadbdcdddfe0e1e2e4e5e6e7e9eaebeceeeff0f1f3f4f5f6', - 'ecb-tbl-256: I=48'), - ('f083388b013679efcf0bb9b15d52ae5c', 'aad5ad50c6262aaec30541a1b7b5b19c', - 'f8f9fafbfdfefe00020304050708090a0c0d0e0f11121314161718191b1c1d1e', - 'ecb-tbl-256: I=49'), - ('c5009e0dab55db0abdb636f2600290c8', '7d34b893855341ec625bd6875ac18c0d', - '20212223252627282a2b2c2d2f30313234353637393a3b3c3e3f404143444546', - 'ecb-tbl-256: I=50'), - ('7804881e26cd532d8514d3683f00f1b9', '7ef05105440f83862f5d780e88f02b41', - '48494a4b4d4e4f50525354555758595a5c5d5e5f61626364666768696b6c6d6e', - 'ecb-tbl-256: I=51'), - ('46cddcd73d1eb53e675ca012870a92a3', 'c377c06403382061af2c9c93a8e70df6', - '70717273757677787a7b7c7d7f80818284858687898a8b8c8e8f909193949596', - 'ecb-tbl-256: I=52'), - ('a9fb44062bb07fe130a8e8299eacb1ab', '1dbdb3ffdc052dacc83318853abc6de5', - '98999a9b9d9e9fa0a2a3a4a5a7a8a9aaacadaeafb1b2b3b4b6b7b8b9bbbcbdbe', - 'ecb-tbl-256: I=53'), - ('2b6ff8d7a5cc3a28a22d5a6f221af26b', '69a6eab00432517d0bf483c91c0963c7', - 'c0c1c2c3c5c6c7c8cacbcccdcfd0d1d2d4d5d6d7d9dadbdcdedfe0e1e3e4e5e6', - 'ecb-tbl-256: I=54'), - ('1a9527c29b8add4b0e3e656dbb2af8b4', '0797f41dc217c80446e1d514bd6ab197', - 'e8e9eaebedeeeff0f2f3f4f5f7f8f9fafcfdfeff01020304060708090b0c0d0e', - 'ecb-tbl-256: I=55'), - ('7f99cf2c75244df015eb4b0c1050aeae', '9dfd76575902a637c01343c58e011a03', - '10111213151617181a1b1c1d1f20212224252627292a2b2c2e2f303133343536', - 'ecb-tbl-256: I=56'), - ('e84ff85b0d9454071909c1381646c4ed', 'acf4328ae78f34b9fa9b459747cc2658', - '38393a3b3d3e3f40424344454748494a4c4d4e4f51525354565758595b5c5d5e', - 'ecb-tbl-256: I=57'), - ('89afd40f99521280d5399b12404f6db4', 'b0479aea12bac4fe2384cf98995150c6', - '60616263656667686a6b6c6d6f70717274757677797a7b7c7e7f808183848586', - 'ecb-tbl-256: I=58'), - ('a09ef32dbc5119a35ab7fa38656f0329', '9dd52789efe3ffb99f33b3da5030109a', - '88898a8b8d8e8f90929394959798999a9c9d9e9fa1a2a3a4a6a7a8a9abacadae', - 'ecb-tbl-256: I=59'), - ('61773457f068c376c7829b93e696e716', 'abbb755e4621ef8f1214c19f649fb9fd', - 'b0b1b2b3b5b6b7b8babbbcbdbfc0c1c2c4c5c6c7c9cacbcccecfd0d1d3d4d5d6', - 'ecb-tbl-256: I=60'), - ('a34f0cae726cce41dd498747d891b967', 'da27fb8174357bce2bed0e7354f380f9', - 'd8d9dadbdddedfe0e2e3e4e5e7e8e9eaecedeeeff1f2f3f4f6f7f8f9fbfcfdfe', - 'ecb-tbl-256: I=61'), - ('856f59496c7388ee2d2b1a27b7697847', 'c59a0663f0993838f6e5856593bdc5ef', - '00010203050607080a0b0c0d0f10111214151617191a1b1c1e1f202123242526', - 'ecb-tbl-256: I=62'), - ('cb090c593ef7720bd95908fb93b49df4', 'ed60b264b5213e831607a99c0ce5e57e', - '28292a2b2d2e2f30323334353738393a3c3d3e3f41424344464748494b4c4d4e', - 'ecb-tbl-256: I=63'), - ('a0ac75cd2f1923d460fc4d457ad95baf', 'e50548746846f3eb77b8c520640884ed', - '50515253555657585a5b5c5d5f60616264656667696a6b6c6e6f707173747576', - 'ecb-tbl-256: I=64'), - ('2a2b282974777689e8e9eeef525d5c5f', '28282cc7d21d6a2923641e52d188ef0c', - '78797a7b7d7e7f80828384858788898a8c8d8e8f91929394969798999b9c9d9e', - 'ecb-tbl-256: I=65'), - ('909192939390919e0f0e09089788898a', '0dfa5b02abb18e5a815305216d6d4f8e', - 'a0a1a2a3a5a6a7a8aaabacadafb0b1b2b4b5b6b7b9babbbcbebfc0c1c3c4c5c6', - 'ecb-tbl-256: I=66'), - ('777675748d8e8f907170777649464744', '7359635c0eecefe31d673395fb46fb99', - 'c8c9cacbcdcecfd0d2d3d4d5d7d8d9dadcdddedfe1e2e3e4e6e7e8e9ebecedee', - 'ecb-tbl-256: I=67'), - ('717073720605040b2d2c2b2a05fafbf9', '73c679f7d5aef2745c9737bb4c47fb36', - 'f0f1f2f3f5f6f7f8fafbfcfdfe01000204050607090a0b0c0e0f101113141516', - 'ecb-tbl-256: I=68'), - ('64656667fefdfcc31b1a1d1ca5aaaba8', 'b192bd472a4d2eafb786e97458967626', - '18191a1b1d1e1f20222324252728292a2c2d2e2f31323334363738393b3c3d3e', - 'ecb-tbl-256: I=69'), - ('dbdad9d86a696867b5b4b3b2c8d7d6d5', '0ec327f6c8a2b147598ca3fde61dc6a4', - '40414243454647484a4b4c4d4f50515254555657595a5b5c5e5f606163646566', - 'ecb-tbl-256: I=70'), - ('5c5d5e5fe3e0e1fe31303736333c3d3e', 'fc418eb3c41b859b38d4b6f646629729', - '68696a6b6d6e6f70727374757778797a7c7d7e7f81828384868788898b8c8d8e', - 'ecb-tbl-256: I=71'), - ('545556574b48494673727574546b6a69', '30249e5ac282b1c981ea64b609f3a154', - '90919293959697989a9b9c9d9fa0a1a2a4a5a6a7a9aaabacaeafb0b1b3b4b5b6', - 'ecb-tbl-256: I=72'), - ('ecedeeefc6c5c4bb56575051f5fafbf8', '5e6e08646d12150776bb43c2d78a9703', - 'b8b9babbbdbebfc0c2c3c4c5c7c8c9cacccdcecfd1d2d3d4d6d7d8d9dbdcddde', - 'ecb-tbl-256: I=73'), - ('464744452724252ac9c8cfced2cdcccf', 'faeb3d5de652cd3447dceb343f30394a', - 'e0e1e2e3e5e6e7e8eaebecedeff0f1f2f4f5f6f7f9fafbfcfefe010103040506', - 'ecb-tbl-256: I=74'), - ('e6e7e4e54142435c878681801c131211', 'a8e88706823f6993ef80d05c1c7b2cf0', - '08090a0b0d0e0f10121314151718191a1c1d1e1f21222324262728292b2c2d2e', - 'ecb-tbl-256: I=75'), - ('72737071cfcccdc2f9f8fffe710e0f0c', '8ced86677e6e00a1a1b15968f2d3cce6', - '30313233353637383a3b3c3d3f40414244454647494a4b4c4e4f505153545556', - 'ecb-tbl-256: I=76'), - ('505152537370714ec3c2c5c4010e0f0c', '9fc7c23858be03bdebb84e90db6786a9', - '58595a5b5d5e5f60626364656768696a6c6d6e6f71727374767778797b7c7d7e', - 'ecb-tbl-256: I=77'), - ('a8a9aaab5c5f5e51aeafa8a93d222320', 'b4fbd65b33f70d8cf7f1111ac4649c36', - '80818283858687888a8b8c8d8f90919294959697999a9b9c9e9fa0a1a3a4a5a6', - 'ecb-tbl-256: I=78'), - ('dedfdcddf6f5f4eb10111617fef1f0f3', 'c5c32d5ed03c4b53cc8c1bd0ef0dbbf6', - 'a8a9aaabadaeafb0b2b3b4b5b7b8b9babcbdbebfc1c2c3c4c6c7c8c9cbcccdce', - 'ecb-tbl-256: I=79'), - ('bdbcbfbe5e5d5c530b0a0d0cfac5c4c7', 'd1a7f03b773e5c212464b63709c6a891', - 'd0d1d2d3d5d6d7d8dadbdcdddfe0e1e2e4e5e6e7e9eaebeceeeff0f1f3f4f5f6', - 'ecb-tbl-256: I=80'), - ('8a8b8889050606f8f4f5f2f3636c6d6e', '6b7161d8745947ac6950438ea138d028', - 'f8f9fafbfdfefe00020304050708090a0c0d0e0f11121314161718191b1c1d1e', - 'ecb-tbl-256: I=81'), - ('a6a7a4a54d4e4f40b2b3b4b539262724', 'fd47a9f7e366ee7a09bc508b00460661', - '20212223252627282a2b2c2d2f30313234353637393a3b3c3e3f404143444546', - 'ecb-tbl-256: I=82'), - ('9c9d9e9fe9eaebf40e0f08099b949596', '00d40b003dc3a0d9310b659b98c7e416', - '48494a4b4d4e4f50525354555758595a5c5d5e5f61626364666768696b6c6d6e', - 'ecb-tbl-256: I=83'), - ('2d2c2f2e1013121dcccdcacbed121310', 'eea4c79dcc8e2bda691f20ac48be0717', - '70717273757677787a7b7c7d7f80818284858687898a8b8c8e8f909193949596', - 'ecb-tbl-256: I=84'), - ('f4f5f6f7edeeefd0eaebecedf7f8f9fa', 'e78f43b11c204403e5751f89d05a2509', - '98999a9b9d9e9fa0a2a3a4a5a7a8a9aaacadaeafb1b2b3b4b6b7b8b9bbbcbdbe', - 'ecb-tbl-256: I=85'), - ('3d3c3f3e282b2a2573727574150a0b08', 'd0f0e3d1f1244bb979931e38dd1786ef', - 'c0c1c2c3c5c6c7c8cacbcccdcfd0d1d2d4d5d6d7d9dadbdcdedfe0e1e3e4e5e6', - 'ecb-tbl-256: I=86'), - ('b6b7b4b5f8fbfae5b4b5b2b3a0afaead', '042e639dc4e1e4dde7b75b749ea6f765', - 'e8e9eaebedeeeff0f2f3f4f5f7f8f9fafcfdfeff01020304060708090b0c0d0e', - 'ecb-tbl-256: I=87'), - ('b7b6b5b4989b9a95878681809ba4a5a6', 'bc032fdd0efe29503a980a7d07ab46a8', - '10111213151617181a1b1c1d1f20212224252627292a2b2c2e2f303133343536', - 'ecb-tbl-256: I=88'), - ('a8a9aaabe5e6e798e9e8efee4748494a', '0c93ac949c0da6446effb86183b6c910', - '38393a3b3d3e3f40424344454748494a4c4d4e4f51525354565758595b5c5d5e', - 'ecb-tbl-256: I=89'), - ('ecedeeefd9dadbd4b9b8bfbe657a7b78', 'e0d343e14da75c917b4a5cec4810d7c2', - '60616263656667686a6b6c6d6f70717274757677797a7b7c7e7f808183848586', - 'ecb-tbl-256: I=90'), - ('7f7e7d7c696a6b74cacbcccd929d9c9f', '0eafb821748408279b937b626792e619', - '88898a8b8d8e8f90929394959798999a9c9d9e9fa1a2a3a4a6a7a8a9abacadae', - 'ecb-tbl-256: I=91'), - ('08090a0b0605040bfffef9f8b9c6c7c4', 'fa1ac6e02d23b106a1fef18b274a553f', - 'b0b1b2b3b5b6b7b8babbbcbdbfc0c1c2c4c5c6c7c9cacbcccecfd0d1d3d4d5d6', - 'ecb-tbl-256: I=92'), - ('08090a0bf1f2f3ccfcfdfafb68676665', '0dadfe019cd12368075507df33c1a1e9', - 'd8d9dadbdddedfe0e2e3e4e5e7e8e9eaecedeeeff1f2f3f4f6f7f8f9fbfcfdfe', - 'ecb-tbl-256: I=93'), - ('cacbc8c93a393837050403020d121310', '3a0879b414465d9ffbaf86b33a63a1b9', - '00010203050607080a0b0c0d0f10111214151617191a1b1c1e1f202123242526', - 'ecb-tbl-256: I=94'), - ('e9e8ebea8281809f8f8e8988343b3a39', '62199fadc76d0be1805d3ba0b7d914bf', - '28292a2b2d2e2f30323334353738393a3c3d3e3f41424344464748494b4c4d4e', - 'ecb-tbl-256: I=95'), - ('515053524645444bd0d1d6d7340b0a09', '1b06d6c5d333e742730130cf78e719b4', - '50515253555657585a5b5c5d5f60616264656667696a6b6c6e6f707173747576', - 'ecb-tbl-256: I=96'), - ('42434041ecefee1193929594c6c9c8cb', 'f1f848824c32e9dcdcbf21580f069329', - '78797a7b7d7e7f80828384858788898a8c8d8e8f91929394969798999b9c9d9e', - 'ecb-tbl-256: I=97'), - ('efeeedecc2c1c0cf76777071455a5b58', '1a09050cbd684f784d8e965e0782f28a', - 'a0a1a2a3a5a6a7a8aaabacadafb0b1b2b4b5b6b7b9babbbcbebfc0c1c3c4c5c6', - 'ecb-tbl-256: I=98'), - ('5f5e5d5c3f3c3d221d1c1b1a19161714', '79c2969e7ded2ba7d088f3f320692360', - 'c8c9cacbcdcecfd0d2d3d4d5d7d8d9dadcdddedfe1e2e3e4e6e7e8e9ebecedee', - 'ecb-tbl-256: I=99'), - ('000102034142434c1c1d1a1b8d727371', '091a658a2f7444c16accb669450c7b63', - 'f0f1f2f3f5f6f7f8fafbfcfdfe01000204050607090a0b0c0e0f101113141516', - 'ecb-tbl-256: I=100'), - ('8e8f8c8db1b2b38c56575051050a0b08', '97c1e3a72cca65fa977d5ed0e8a7bbfc', - '18191a1b1d1e1f20222324252728292a2c2d2e2f31323334363738393b3c3d3e', - 'ecb-tbl-256: I=101'), - ('a7a6a5a4e8ebeae57f7e7978cad5d4d7', '70c430c6db9a17828937305a2df91a2a', - '40414243454647484a4b4c4d4f50515254555657595a5b5c5e5f606163646566', - 'ecb-tbl-256: I=102'), - ('8a8b888994979689454443429f909192', '629553457fbe2479098571c7c903fde8', - '68696a6b6d6e6f70727374757778797a7c7d7e7f81828384868788898b8c8d8e', - 'ecb-tbl-256: I=103'), - ('8c8d8e8fe0e3e2ed45444342f1cecfcc', 'a25b25a61f612669e7d91265c7d476ba', - '90919293959697989a9b9c9d9fa0a1a2a4a5a6a7a9aaabacaeafb0b1b3b4b5b6', - 'ecb-tbl-256: I=104'), - ('fffefdfc4c4f4e31d8d9dedfb6b9b8bb', 'eb7e4e49b8ae0f024570dda293254fed', - 'b8b9babbbdbebfc0c2c3c4c5c7c8c9cacccdcecfd1d2d3d4d6d7d8d9dbdcddde', - 'ecb-tbl-256: I=105'), - ('fdfcfffecccfcec12f2e29286679787b', '38fe15d61cca84516e924adce5014f67', - 'e0e1e2e3e5e6e7e8eaebecedeff0f1f2f4f5f6f7f9fafbfcfefe010103040506', - 'ecb-tbl-256: I=106'), - ('67666564bab9b8a77071767719161714', '3ad208492249108c9f3ebeb167ad0583', - '08090a0b0d0e0f10121314151718191a1c1d1e1f21222324262728292b2c2d2e', - 'ecb-tbl-256: I=107'), - ('9a9b98992d2e2f2084858283245b5a59', '299ba9f9bf5ab05c3580fc26edd1ed12', - '30313233353637383a3b3c3d3f40414244454647494a4b4c4e4f505153545556', - 'ecb-tbl-256: I=108'), - ('a4a5a6a70b0809365c5d5a5b2c232221', '19dc705b857a60fb07717b2ea5717781', - '58595a5b5d5e5f60626364656768696a6c6d6e6f71727374767778797b7c7d7e', - 'ecb-tbl-256: I=109'), - ('464744455754555af3f2f5f4afb0b1b2', 'ffc8aeb885b5efcad06b6dbebf92e76b', - '80818283858687888a8b8c8d8f90919294959697999a9b9c9e9fa0a1a3a4a5a6', - 'ecb-tbl-256: I=110'), - ('323330317675746b7273747549464744', 'f58900c5e0b385253ff2546250a0142b', - 'a8a9aaabadaeafb0b2b3b4b5b7b8b9babcbdbebfc1c2c3c4c6c7c8c9cbcccdce', - 'ecb-tbl-256: I=111'), - ('a8a9aaab181b1a15808186872b141516', '2ee67b56280bc462429cee6e3370cbc1', - 'd0d1d2d3d5d6d7d8dadbdcdddfe0e1e2e4e5e6e7e9eaebeceeeff0f1f3f4f5f6', - 'ecb-tbl-256: I=112'), - ('e7e6e5e4202323ddaaabacad343b3a39', '20db650a9c8e9a84ab4d25f7edc8f03f', - 'f8f9fafbfdfefe00020304050708090a0c0d0e0f11121314161718191b1c1d1e', - 'ecb-tbl-256: I=113'), - ('a8a9aaab2221202fedecebea1e010003', '3c36da169525cf818843805f25b78ae5', - '20212223252627282a2b2c2d2f30313234353637393a3b3c3e3f404143444546', - 'ecb-tbl-256: I=114'), - ('f9f8fbfa5f5c5d42424344450e010003', '9a781d960db9e45e37779042fea51922', - '48494a4b4d4e4f50525354555758595a5c5d5e5f61626364666768696b6c6d6e', - 'ecb-tbl-256: I=115'), - ('57565554f5f6f7f89697909120dfdedd', '6560395ec269c672a3c288226efdba77', - '70717273757677787a7b7c7d7f80818284858687898a8b8c8e8f909193949596', - 'ecb-tbl-256: I=116'), - ('f8f9fafbcccfcef1dddcdbda0e010003', '8c772b7a189ac544453d5916ebb27b9a', - '98999a9b9d9e9fa0a2a3a4a5a7a8a9aaacadaeafb1b2b3b4b6b7b8b9bbbcbdbe', - 'ecb-tbl-256: I=117'), - ('d9d8dbda7073727d80818687c2dddcdf', '77ca5468cc48e843d05f78eed9d6578f', - 'c0c1c2c3c5c6c7c8cacbcccdcfd0d1d2d4d5d6d7d9dadbdcdedfe0e1e3e4e5e6', - 'ecb-tbl-256: I=118'), - ('c5c4c7c6080b0a1588898e8f68676665', '72cdcc71dc82c60d4429c9e2d8195baa', - 'e8e9eaebedeeeff0f2f3f4f5f7f8f9fafcfdfeff01020304060708090b0c0d0e', - 'ecb-tbl-256: I=119'), - ('83828180dcdfded186878081f0cfcecd', '8080d68ce60e94b40b5b8b69eeb35afa', - '10111213151617181a1b1c1d1f20212224252627292a2b2c2e2f303133343536', - 'ecb-tbl-256: I=120'), - ('98999a9bdddedfa079787f7e0a050407', '44222d3cde299c04369d58ac0eba1e8e', - '38393a3b3d3e3f40424344454748494a4c4d4e4f51525354565758595b5c5d5e', - 'ecb-tbl-256: I=121'), - ('cecfcccd4f4c4d429f9e9998dfc0c1c2', '9b8721b0a8dfc691c5bc5885dbfcb27a', - '60616263656667686a6b6c6d6f70717274757677797a7b7c7e7f808183848586', - 'ecb-tbl-256: I=122'), - ('404142436665647b29282f2eaba4a5a6', '0dc015ce9a3a3414b5e62ec643384183', - '88898a8b8d8e8f90929394959798999a9c9d9e9fa1a2a3a4a6a7a8a9abacadae', - 'ecb-tbl-256: I=123'), - ('33323130e6e5e4eb23222524dea1a0a3', '705715448a8da412025ce38345c2a148', - 'b0b1b2b3b5b6b7b8babbbcbdbfc0c1c2c4c5c6c7c9cacbcccecfd0d1d3d4d5d6', - 'ecb-tbl-256: I=124'), - ('cfcecdccf6f5f4cbe6e7e0e199969794', 'c32b5b0b6fbae165266c569f4b6ecf0b', - 'd8d9dadbdddedfe0e2e3e4e5e7e8e9eaecedeeeff1f2f3f4f6f7f8f9fbfcfdfe', - 'ecb-tbl-256: I=125'), - ('babbb8b97271707fdcdddadb29363734', '4dca6c75192a01ddca9476af2a521e87', - '00010203050607080a0b0c0d0f10111214151617191a1b1c1e1f202123242526', - 'ecb-tbl-256: I=126'), - ('c9c8cbca4447465926272021545b5a59', '058691e627ecbc36ac07b6db423bd698', - '28292a2b2d2e2f30323334353738393a3c3d3e3f41424344464748494b4c4d4e', - 'ecb-tbl-256: I=127'), - ('050407067477767956575051221d1c1f', '7444527095838fe080fc2bcdd30847eb', - '50515253555657585a5b5c5d5f60616264656667696a6b6c6e6f707173747576', - 'ecb-tbl-256: I=128'), - - # FIPS PUB 800-38A test vectors, 2001 edition. Annex F. - - ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ - '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17ad2b417be66c3710', - '3ad77bb40d7a3660a89ecaf32466ef97'+'f5d3d58503b9699de785895a96fdbaaf'+ - '43b1cd7f598ece23881b00e3ed030688'+'7b0c785e27e8ad3f8223207104725dd4', - '2b7e151628aed2a6abf7158809cf4f3c', - 'NIST 800-38A, F.1.1, ECB and AES-128'), - - ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ - '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17ad2b417be66c3710', - 'bd334f1d6e45f25ff712a214571fa5cc'+'974104846d0ad3ad7734ecb3ecee4eef'+ - 'ef7afd2270e2e60adce0ba2face6444e'+'9a4b41ba738d6c72fb16691603c18e0e', - '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b', - 'NIST 800-38A, F.1.3, ECB and AES-192'), - - ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ - '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17ad2b417be66c3710', - 'f3eed1bdb5d2a03c064b5a7e3db181f8'+'591ccb10d410ed26dc5ba74a31362870'+ - 'b6ed21b99ca6f4f9f153e7b1beafed1d'+'23304b7a39f9f3ff067d8d8f9e24ecc7', - '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4', - 'NIST 800-38A, F.1.3, ECB and AES-256'), - -] - -test_data_8_lanes = [] -for td in test_data: - test_data_8_lanes.append((td[0] * 8, td[1] * 8, td[2], td[3])) -test_data += test_data_8_lanes - -class TestMultipleBlocks(unittest.TestCase): - - def __init__(self, use_aesni): - unittest.TestCase.__init__(self) - self.use_aesni = use_aesni - - def runTest(self): - # Encrypt data which is 8*2+4 bytes long, so as to trigger (for the - # AESNI variant) both the path that parallelizes 8 lanes and the one - # that processes data serially - - tvs = [ - (b'a' * 16, 'c0b27011eb15bf144d2fc9fae80ea16d4c231cb230416c5fac02e6835ad9d7d0'), - (b'a' * 24, 'df8435ce361a78c535b41dcb57da952abbf9ee5954dc6fbcd75fd00fa626915d'), - (b'a' * 32, '211402de6c80db1f92ba255881178e1f70783b8cfd3b37808205e48b80486cd8') - ] - - for key, expected in tvs: - - cipher = AES.new(key, AES.MODE_ECB, use_aesni=self.use_aesni) - h = SHA256.new() - - pt = b"".join([ tobytes('{0:016x}'.format(x)) for x in range(20) ]) - ct = cipher.encrypt(pt) - self.assertEqual(SHA256.new(ct).hexdigest(), expected) - - -class TestIncompleteBlocks(unittest.TestCase): - - def __init__(self, use_aesni): - unittest.TestCase.__init__(self) - self.use_aesni = use_aesni - - def runTest(self): - # Encrypt data with length not multiple of 16 bytes - - cipher = AES.new(b'4'*16, AES.MODE_ECB, use_aesni=self.use_aesni) - - for msg_len in range(1, 16): - self.assertRaises(ValueError, cipher.encrypt, b'1' * msg_len) - self.assertRaises(ValueError, cipher.encrypt, b'1' * (msg_len+16)) - self.assertRaises(ValueError, cipher.decrypt, b'1' * msg_len) - self.assertRaises(ValueError, cipher.decrypt, b'1' * (msg_len+16)) - - self.assertEqual(cipher.encrypt(b''), b'') - self.assertEqual(cipher.decrypt(b''), b'') - - -class TestOutput(unittest.TestCase): - - def __init__(self, use_aesni): - unittest.TestCase.__init__(self) - self.use_aesni = use_aesni - - def runTest(self): - # Encrypt/Decrypt data and test output parameter - - cipher = AES.new(b'4'*16, AES.MODE_ECB, use_aesni=self.use_aesni) - - pt = b'5' * 16 - ct = cipher.encrypt(pt) - - output = bytearray(16) - res = cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - self.assertEqual(res, None) - - res = cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - self.assertEqual(res, None) - - output = memoryview(bytearray(16)) - cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - - cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - - self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0'*16) - self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0'*16) - - shorter_output = bytearray(15) - self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output) - self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output) - - -def get_tests(config={}): - from Cryptodome.Util import _cpu_features - from .common import make_block_tests - - tests = make_block_tests(AES, "AES", test_data, {'use_aesni': False}) - tests += [ TestMultipleBlocks(False) ] - tests += [ TestIncompleteBlocks(False) ] - if _cpu_features.have_aes_ni(): - # Run tests with AES-NI instructions if they are available. - tests += make_block_tests(AES, "AESNI", test_data, {'use_aesni': True}) - tests += [ TestMultipleBlocks(True) ] - tests += [ TestIncompleteBlocks(True) ] - tests += [ TestOutput(True) ] - else: - print("Skipping AESNI tests") - return tests - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_ARC2.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_ARC2.py deleted file mode 100644 index 0072506..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_ARC2.py +++ /dev/null @@ -1,167 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Cipher/ARC2.py: Self-test for the Alleged-RC2 cipher -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Cipher.ARC2""" - -import unittest - -from Cryptodome.Util.py3compat import b, bchr - -from Cryptodome.Cipher import ARC2 - -# This is a list of (plaintext, ciphertext, key[, description[, extra_params]]) tuples. -test_data = [ - # Test vectors from RFC 2268 - - # 63-bit effective key length - ('0000000000000000', 'ebb773f993278eff', '0000000000000000', - 'RFC2268-1', dict(effective_keylen=63)), - - # 64-bit effective key length - ('ffffffffffffffff', '278b27e42e2f0d49', 'ffffffffffffffff', - 'RFC2268-2', dict(effective_keylen=64)), - ('1000000000000001', '30649edf9be7d2c2', '3000000000000000', - 'RFC2268-3', dict(effective_keylen=64)), - #('0000000000000000', '61a8a244adacccf0', '88', - # 'RFC2268-4', dict(effective_keylen=64)), - ('0000000000000000', '6ccf4308974c267f', '88bca90e90875a', - 'RFC2268-5', dict(effective_keylen=64)), - ('0000000000000000', '1a807d272bbe5db1', '88bca90e90875a7f0f79c384627bafb2', - 'RFC2268-6', dict(effective_keylen=64)), - - # 128-bit effective key length - ('0000000000000000', '2269552ab0f85ca6', '88bca90e90875a7f0f79c384627bafb2', - "RFC2268-7", dict(effective_keylen=128)), - ('0000000000000000', '5b78d3a43dfff1f1', - '88bca90e90875a7f0f79c384627bafb216f80a6f85920584c42fceb0be255daf1e', - "RFC2268-8", dict(effective_keylen=129)), - - # Test vectors from PyCryptodome 2.0.1's testdata.py - # 1024-bit effective key length - ('0000000000000000', '624fb3e887419e48', '5068696c6970476c617373', - 'PCTv201-0'), - ('ffffffffffffffff', '79cadef44c4a5a85', '5068696c6970476c617373', - 'PCTv201-1'), - ('0001020304050607', '90411525b34e4c2c', '5068696c6970476c617373', - 'PCTv201-2'), - ('0011223344556677', '078656aaba61cbfb', '5068696c6970476c617373', - 'PCTv201-3'), - ('0000000000000000', 'd7bcc5dbb4d6e56a', 'ffffffffffffffff', - 'PCTv201-4'), - ('ffffffffffffffff', '7259018ec557b357', 'ffffffffffffffff', - 'PCTv201-5'), - ('0001020304050607', '93d20a497f2ccb62', 'ffffffffffffffff', - 'PCTv201-6'), - ('0011223344556677', 'cb15a7f819c0014d', 'ffffffffffffffff', - 'PCTv201-7'), - ('0000000000000000', '63ac98cdf3843a7a', 'ffffffffffffffff5065746572477265656e6177617953e5ffe553', - 'PCTv201-8'), - ('ffffffffffffffff', '3fb49e2fa12371dd', 'ffffffffffffffff5065746572477265656e6177617953e5ffe553', - 'PCTv201-9'), - ('0001020304050607', '46414781ab387d5f', 'ffffffffffffffff5065746572477265656e6177617953e5ffe553', - 'PCTv201-10'), - ('0011223344556677', 'be09dc81feaca271', 'ffffffffffffffff5065746572477265656e6177617953e5ffe553', - 'PCTv201-11'), - ('0000000000000000', 'e64221e608be30ab', '53e5ffe553', - 'PCTv201-12'), - ('ffffffffffffffff', '862bc60fdcd4d9a9', '53e5ffe553', - 'PCTv201-13'), - ('0001020304050607', '6a34da50fa5e47de', '53e5ffe553', - 'PCTv201-14'), - ('0011223344556677', '584644c34503122c', '53e5ffe553', - 'PCTv201-15'), -] - -class BufferOverflowTest(unittest.TestCase): - # Test a buffer overflow found in older versions of PyCrypto - - def runTest(self): - """ARC2 with keylength > 128""" - key = b("x") * 16384 - self.assertRaises(ValueError, ARC2.new, key, ARC2.MODE_ECB) - -class KeyLength(unittest.TestCase): - - def runTest(self): - ARC2.new(b'\x00' * 16, ARC2.MODE_ECB, effective_keylen=40) - self.assertRaises(ValueError, ARC2.new, bchr(0) * 4, ARC2.MODE_ECB) - self.assertRaises(ValueError, ARC2.new, bchr(0) * 129, ARC2.MODE_ECB) - - self.assertRaises(ValueError, ARC2.new, bchr(0) * 16, ARC2.MODE_ECB, - effective_keylen=39) - self.assertRaises(ValueError, ARC2.new, bchr(0) * 16, ARC2.MODE_ECB, - effective_keylen=1025) - - -class TestOutput(unittest.TestCase): - - def runTest(self): - # Encrypt/Decrypt data and test output parameter - - cipher = ARC2.new(b'4'*16, ARC2.MODE_ECB) - - pt = b'5' * 16 - ct = cipher.encrypt(pt) - - output = bytearray(16) - res = cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - self.assertEqual(res, None) - - res = cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - self.assertEqual(res, None) - - output = memoryview(bytearray(16)) - cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - - cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - - self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0'*16) - self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0'*16) - - shorter_output = bytearray(7) - self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output) - self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output) - - -def get_tests(config={}): - from Cryptodome.Cipher import ARC2 - from .common import make_block_tests - - tests = make_block_tests(ARC2, "ARC2", test_data) - tests.append(BufferOverflowTest()) - tests.append(KeyLength()) - tests += [TestOutput()] - - return tests - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_ARC4.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_ARC4.py deleted file mode 100644 index a160c98..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_ARC4.py +++ /dev/null @@ -1,471 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Cipher/ARC4.py: Self-test for the Alleged-RC4 cipher -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Cipher.ARC4""" - -import unittest - -from Cryptodome.Util.py3compat import b -from Cryptodome.SelfTest.st_common import list_test_cases -from binascii import unhexlify - -from Cryptodome.Cipher import ARC4 - -# This is a list of (plaintext, ciphertext, key[, description]) tuples. -test_data = [ - # Test vectors from Eric Rescorla's message with the subject - # "RC4 compatibility testing", sent to the cipherpunks mailing list on - # September 13, 1994. - # http://cypherpunks.venona.com/date/1994/09/msg00420.html - - ('0123456789abcdef', '75b7878099e0c596', '0123456789abcdef', - 'Test vector 0'), - - ('0000000000000000', '7494c2e7104b0879', '0123456789abcdef', - 'Test vector 1'), - - ('0000000000000000', 'de188941a3375d3a', '0000000000000000', - 'Test vector 2'), - - ('00000000000000000000', 'd6a141a7ec3c38dfbd61', 'ef012345', - 'Test vector 3'), - - ('01' * 512, - '7595c3e6114a09780c4ad452338e1ffd9a1be9498f813d76533449b6778dcad8' - + 'c78a8d2ba9ac66085d0e53d59c26c2d1c490c1ebbe0ce66d1b6b1b13b6b919b8' - + '47c25a91447a95e75e4ef16779cde8bf0a95850e32af9689444fd377108f98fd' - + 'cbd4e726567500990bcc7e0ca3c4aaa304a387d20f3b8fbbcd42a1bd311d7a43' - + '03dda5ab078896ae80c18b0af66dff319616eb784e495ad2ce90d7f772a81747' - + 'b65f62093b1e0db9e5ba532fafec47508323e671327df9444432cb7367cec82f' - + '5d44c0d00b67d650a075cd4b70dedd77eb9b10231b6b5b741347396d62897421' - + 'd43df9b42e446e358e9c11a9b2184ecbef0cd8e7a877ef968f1390ec9b3d35a5' - + '585cb009290e2fcde7b5ec66d9084be44055a619d9dd7fc3166f9487f7cb2729' - + '12426445998514c15d53a18c864ce3a2b7555793988126520eacf2e3066e230c' - + '91bee4dd5304f5fd0405b35bd99c73135d3d9bc335ee049ef69b3867bf2d7bd1' - + 'eaa595d8bfc0066ff8d31509eb0c6caa006c807a623ef84c3d33c195d23ee320' - + 'c40de0558157c822d4b8c569d849aed59d4e0fd7f379586b4b7ff684ed6a189f' - + '7486d49b9c4bad9ba24b96abf924372c8a8fffb10d55354900a77a3db5f205e1' - + 'b99fcd8660863a159ad4abe40fa48934163ddde542a6585540fd683cbfd8c00f' - + '12129a284deacc4cdefe58be7137541c047126c8d49e2755ab181ab7e940b0c0', - '0123456789abcdef', - "Test vector 4"), - # shortest key - generated with arc4 package - ('7468697320697320616e206578616d706c65', - '7260677d38495a09585d69321e17eaf3cdd0', - '01'), -] - - -class RFC6229_Tests(unittest.TestCase): - # Test vectors from RFC 6229. Each test vector is a tuple with two items: - # the ARC4 key and a dictionary. The dictionary has keystream offsets as keys - # and the 16-byte keystream starting at the relevant offset as value. - rfc6229_data = [ - # Page 3 - ( - '0102030405', - { - 0: 'b2 39 63 05 f0 3d c0 27 cc c3 52 4a 0a 11 18 a8', - 16: '69 82 94 4f 18 fc 82 d5 89 c4 03 a4 7a 0d 09 19', - 240: '28 cb 11 32 c9 6c e2 86 42 1d ca ad b8 b6 9e ae', - 256: '1c fc f6 2b 03 ed db 64 1d 77 df cf 7f 8d 8c 93', - 496: '42 b7 d0 cd d9 18 a8 a3 3d d5 17 81 c8 1f 40 41', - 512: '64 59 84 44 32 a7 da 92 3c fb 3e b4 98 06 61 f6', - 752: 'ec 10 32 7b de 2b ee fd 18 f9 27 76 80 45 7e 22', - 768: 'eb 62 63 8d 4f 0b a1 fe 9f ca 20 e0 5b f8 ff 2b', - 1008: '45 12 90 48 e6 a0 ed 0b 56 b4 90 33 8f 07 8d a5', - 1024: '30 ab bc c7 c2 0b 01 60 9f 23 ee 2d 5f 6b b7 df', - 1520: '32 94 f7 44 d8 f9 79 05 07 e7 0f 62 e5 bb ce ea', - 1536: 'd8 72 9d b4 18 82 25 9b ee 4f 82 53 25 f5 a1 30', - 2032: '1e b1 4a 0c 13 b3 bf 47 fa 2a 0b a9 3a d4 5b 8b', - 2048: 'cc 58 2f 8b a9 f2 65 e2 b1 be 91 12 e9 75 d2 d7', - 3056: 'f2 e3 0f 9b d1 02 ec bf 75 aa ad e9 bc 35 c4 3c', - 3072: 'ec 0e 11 c4 79 dc 32 9d c8 da 79 68 fe 96 56 81', - 4080: '06 83 26 a2 11 84 16 d2 1f 9d 04 b2 cd 1c a0 50', - 4096: 'ff 25 b5 89 95 99 67 07 e5 1f bd f0 8b 34 d8 75' - } - ), - # Page 4 - ( - '01020304050607', - { - 0: '29 3f 02 d4 7f 37 c9 b6 33 f2 af 52 85 fe b4 6b', - 16: 'e6 20 f1 39 0d 19 bd 84 e2 e0 fd 75 20 31 af c1', - 240: '91 4f 02 53 1c 92 18 81 0d f6 0f 67 e3 38 15 4c', - 256: 'd0 fd b5 83 07 3c e8 5a b8 39 17 74 0e c0 11 d5', - 496: '75 f8 14 11 e8 71 cf fa 70 b9 0c 74 c5 92 e4 54', - 512: '0b b8 72 02 93 8d ad 60 9e 87 a5 a1 b0 79 e5 e4', - 752: 'c2 91 12 46 b6 12 e7 e7 b9 03 df ed a1 da d8 66', - 768: '32 82 8f 91 50 2b 62 91 36 8d e8 08 1d e3 6f c2', - 1008: 'f3 b9 a7 e3 b2 97 bf 9a d8 04 51 2f 90 63 ef f1', - 1024: '8e cb 67 a9 ba 1f 55 a5 a0 67 e2 b0 26 a3 67 6f', - 1520: 'd2 aa 90 2b d4 2d 0d 7c fd 34 0c d4 58 10 52 9f', - 1536: '78 b2 72 c9 6e 42 ea b4 c6 0b d9 14 e3 9d 06 e3', - 2032: 'f4 33 2f d3 1a 07 93 96 ee 3c ee 3f 2a 4f f0 49', - 2048: '05 45 97 81 d4 1f da 7f 30 c1 be 7e 12 46 c6 23', - 3056: 'ad fd 38 68 b8 e5 14 85 d5 e6 10 01 7e 3d d6 09', - 3072: 'ad 26 58 1c 0c 5b e4 5f 4c ea 01 db 2f 38 05 d5', - 4080: 'f3 17 2c ef fc 3b 3d 99 7c 85 cc d5 af 1a 95 0c', - 4096: 'e7 4b 0b 97 31 22 7f d3 7c 0e c0 8a 47 dd d8 b8' - } - ), - ( - '0102030405060708', - { - 0: '97 ab 8a 1b f0 af b9 61 32 f2 f6 72 58 da 15 a8', - 16: '82 63 ef db 45 c4 a1 86 84 ef 87 e6 b1 9e 5b 09', - 240: '96 36 eb c9 84 19 26 f4 f7 d1 f3 62 bd df 6e 18', - 256: 'd0 a9 90 ff 2c 05 fe f5 b9 03 73 c9 ff 4b 87 0a', - 496: '73 23 9f 1d b7 f4 1d 80 b6 43 c0 c5 25 18 ec 63', - 512: '16 3b 31 99 23 a6 bd b4 52 7c 62 61 26 70 3c 0f', - 752: '49 d6 c8 af 0f 97 14 4a 87 df 21 d9 14 72 f9 66', - 768: '44 17 3a 10 3b 66 16 c5 d5 ad 1c ee 40 c8 63 d0', - 1008: '27 3c 9c 4b 27 f3 22 e4 e7 16 ef 53 a4 7d e7 a4', - 1024: 'c6 d0 e7 b2 26 25 9f a9 02 34 90 b2 61 67 ad 1d', - 1520: '1f e8 98 67 13 f0 7c 3d 9a e1 c1 63 ff 8c f9 d3', - 1536: '83 69 e1 a9 65 61 0b e8 87 fb d0 c7 91 62 aa fb', - 2032: '0a 01 27 ab b4 44 84 b9 fb ef 5a bc ae 1b 57 9f', - 2048: 'c2 cd ad c6 40 2e 8e e8 66 e1 f3 7b db 47 e4 2c', - 3056: '26 b5 1e a3 7d f8 e1 d6 f7 6f c3 b6 6a 74 29 b3', - 3072: 'bc 76 83 20 5d 4f 44 3d c1 f2 9d da 33 15 c8 7b', - 4080: 'd5 fa 5a 34 69 d2 9a aa f8 3d 23 58 9d b8 c8 5b', - 4096: '3f b4 6e 2c 8f 0f 06 8e dc e8 cd cd 7d fc 58 62' - } - ), - # Page 5 - ( - '0102030405060708090a', - { - 0: 'ed e3 b0 46 43 e5 86 cc 90 7d c2 18 51 70 99 02', - 16: '03 51 6b a7 8f 41 3b eb 22 3a a5 d4 d2 df 67 11', - 240: '3c fd 6c b5 8e e0 fd de 64 01 76 ad 00 00 04 4d', - 256: '48 53 2b 21 fb 60 79 c9 11 4c 0f fd 9c 04 a1 ad', - 496: '3e 8c ea 98 01 71 09 97 90 84 b1 ef 92 f9 9d 86', - 512: 'e2 0f b4 9b db 33 7e e4 8b 8d 8d c0 f4 af ef fe', - 752: '5c 25 21 ea cd 79 66 f1 5e 05 65 44 be a0 d3 15', - 768: 'e0 67 a7 03 19 31 a2 46 a6 c3 87 5d 2f 67 8a cb', - 1008: 'a6 4f 70 af 88 ae 56 b6 f8 75 81 c0 e2 3e 6b 08', - 1024: 'f4 49 03 1d e3 12 81 4e c6 f3 19 29 1f 4a 05 16', - 1520: 'bd ae 85 92 4b 3c b1 d0 a2 e3 3a 30 c6 d7 95 99', - 1536: '8a 0f ed db ac 86 5a 09 bc d1 27 fb 56 2e d6 0a', - 2032: 'b5 5a 0a 5b 51 a1 2a 8b e3 48 99 c3 e0 47 51 1a', - 2048: 'd9 a0 9c ea 3c e7 5f e3 96 98 07 03 17 a7 13 39', - 3056: '55 22 25 ed 11 77 f4 45 84 ac 8c fa 6c 4e b5 fc', - 3072: '7e 82 cb ab fc 95 38 1b 08 09 98 44 21 29 c2 f8', - 4080: '1f 13 5e d1 4c e6 0a 91 36 9d 23 22 be f2 5e 3c', - 4096: '08 b6 be 45 12 4a 43 e2 eb 77 95 3f 84 dc 85 53' - } - ), - ( - '0102030405060708090a0b0c0d0e0f10', - { - 0: '9a c7 cc 9a 60 9d 1e f7 b2 93 28 99 cd e4 1b 97', - 16: '52 48 c4 95 90 14 12 6a 6e 8a 84 f1 1d 1a 9e 1c', - 240: '06 59 02 e4 b6 20 f6 cc 36 c8 58 9f 66 43 2f 2b', - 256: 'd3 9d 56 6b c6 bc e3 01 07 68 15 15 49 f3 87 3f', - 496: 'b6 d1 e6 c4 a5 e4 77 1c ad 79 53 8d f2 95 fb 11', - 512: 'c6 8c 1d 5c 55 9a 97 41 23 df 1d bc 52 a4 3b 89', - 752: 'c5 ec f8 8d e8 97 fd 57 fe d3 01 70 1b 82 a2 59', - 768: 'ec cb e1 3d e1 fc c9 1c 11 a0 b2 6c 0b c8 fa 4d', - 1008: 'e7 a7 25 74 f8 78 2a e2 6a ab cf 9e bc d6 60 65', - 1024: 'bd f0 32 4e 60 83 dc c6 d3 ce dd 3c a8 c5 3c 16', - 1520: 'b4 01 10 c4 19 0b 56 22 a9 61 16 b0 01 7e d2 97', - 1536: 'ff a0 b5 14 64 7e c0 4f 63 06 b8 92 ae 66 11 81', - 2032: 'd0 3d 1b c0 3c d3 3d 70 df f9 fa 5d 71 96 3e bd', - 2048: '8a 44 12 64 11 ea a7 8b d5 1e 8d 87 a8 87 9b f5', - 3056: 'fa be b7 60 28 ad e2 d0 e4 87 22 e4 6c 46 15 a3', - 3072: 'c0 5d 88 ab d5 03 57 f9 35 a6 3c 59 ee 53 76 23', - 4080: 'ff 38 26 5c 16 42 c1 ab e8 d3 c2 fe 5e 57 2b f8', - 4096: 'a3 6a 4c 30 1a e8 ac 13 61 0c cb c1 22 56 ca cc' - } - ), - # Page 6 - ( - '0102030405060708090a0b0c0d0e0f101112131415161718', - { - 0: '05 95 e5 7f e5 f0 bb 3c 70 6e da c8 a4 b2 db 11', - 16: 'df de 31 34 4a 1a f7 69 c7 4f 07 0a ee 9e 23 26', - 240: 'b0 6b 9b 1e 19 5d 13 d8 f4 a7 99 5c 45 53 ac 05', - 256: '6b d2 37 8e c3 41 c9 a4 2f 37 ba 79 f8 8a 32 ff', - 496: 'e7 0b ce 1d f7 64 5a db 5d 2c 41 30 21 5c 35 22', - 512: '9a 57 30 c7 fc b4 c9 af 51 ff da 89 c7 f1 ad 22', - 752: '04 85 05 5f d4 f6 f0 d9 63 ef 5a b9 a5 47 69 82', - 768: '59 1f c6 6b cd a1 0e 45 2b 03 d4 55 1f 6b 62 ac', - 1008: '27 53 cc 83 98 8a fa 3e 16 88 a1 d3 b4 2c 9a 02', - 1024: '93 61 0d 52 3d 1d 3f 00 62 b3 c2 a3 bb c7 c7 f0', - 1520: '96 c2 48 61 0a ad ed fe af 89 78 c0 3d e8 20 5a', - 1536: '0e 31 7b 3d 1c 73 b9 e9 a4 68 8f 29 6d 13 3a 19', - 2032: 'bd f0 e6 c3 cc a5 b5 b9 d5 33 b6 9c 56 ad a1 20', - 2048: '88 a2 18 b6 e2 ec e1 e6 24 6d 44 c7 59 d1 9b 10', - 3056: '68 66 39 7e 95 c1 40 53 4f 94 26 34 21 00 6e 40', - 3072: '32 cb 0a 1e 95 42 c6 b3 b8 b3 98 ab c3 b0 f1 d5', - 4080: '29 a0 b8 ae d5 4a 13 23 24 c6 2e 42 3f 54 b4 c8', - 4096: '3c b0 f3 b5 02 0a 98 b8 2a f9 fe 15 44 84 a1 68' - } - ), - ( - '0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20', - { - 0: 'ea a6 bd 25 88 0b f9 3d 3f 5d 1e 4c a2 61 1d 91', - 16: 'cf a4 5c 9f 7e 71 4b 54 bd fa 80 02 7c b1 43 80', - 240: '11 4a e3 44 de d7 1b 35 f2 e6 0f eb ad 72 7f d8', - 256: '02 e1 e7 05 6b 0f 62 39 00 49 64 22 94 3e 97 b6', - 496: '91 cb 93 c7 87 96 4e 10 d9 52 7d 99 9c 6f 93 6b', - 512: '49 b1 8b 42 f8 e8 36 7c be b5 ef 10 4b a1 c7 cd', - 752: '87 08 4b 3b a7 00 ba de 95 56 10 67 27 45 b3 74', - 768: 'e7 a7 b9 e9 ec 54 0d 5f f4 3b db 12 79 2d 1b 35', - 1008: 'c7 99 b5 96 73 8f 6b 01 8c 76 c7 4b 17 59 bd 90', - 1024: '7f ec 5b fd 9f 9b 89 ce 65 48 30 90 92 d7 e9 58', - 1520: '40 f2 50 b2 6d 1f 09 6a 4a fd 4c 34 0a 58 88 15', - 1536: '3e 34 13 5c 79 db 01 02 00 76 76 51 cf 26 30 73', - 2032: 'f6 56 ab cc f8 8d d8 27 02 7b 2c e9 17 d4 64 ec', - 2048: '18 b6 25 03 bf bc 07 7f ba bb 98 f2 0d 98 ab 34', - 3056: '8a ed 95 ee 5b 0d cb fb ef 4e b2 1d 3a 3f 52 f9', - 3072: '62 5a 1a b0 0e e3 9a 53 27 34 6b dd b0 1a 9c 18', - 4080: 'a1 3a 7c 79 c7 e1 19 b5 ab 02 96 ab 28 c3 00 b9', - 4096: 'f3 e4 c0 a2 e0 2d 1d 01 f7 f0 a7 46 18 af 2b 48' - } - ), - # Page 7 - ( - '833222772a', - { - 0: '80 ad 97 bd c9 73 df 8a 2e 87 9e 92 a4 97 ef da', - 16: '20 f0 60 c2 f2 e5 12 65 01 d3 d4 fe a1 0d 5f c0', - 240: 'fa a1 48 e9 90 46 18 1f ec 6b 20 85 f3 b2 0e d9', - 256: 'f0 da f5 ba b3 d5 96 83 98 57 84 6f 73 fb fe 5a', - 496: '1c 7e 2f c4 63 92 32 fe 29 75 84 b2 96 99 6b c8', - 512: '3d b9 b2 49 40 6c c8 ed ff ac 55 cc d3 22 ba 12', - 752: 'e4 f9 f7 e0 06 61 54 bb d1 25 b7 45 56 9b c8 97', - 768: '75 d5 ef 26 2b 44 c4 1a 9c f6 3a e1 45 68 e1 b9', - 1008: '6d a4 53 db f8 1e 82 33 4a 3d 88 66 cb 50 a1 e3', - 1024: '78 28 d0 74 11 9c ab 5c 22 b2 94 d7 a9 bf a0 bb', - 1520: 'ad b8 9c ea 9a 15 fb e6 17 29 5b d0 4b 8c a0 5c', - 1536: '62 51 d8 7f d4 aa ae 9a 7e 4a d5 c2 17 d3 f3 00', - 2032: 'e7 11 9b d6 dd 9b 22 af e8 f8 95 85 43 28 81 e2', - 2048: '78 5b 60 fd 7e c4 e9 fc b6 54 5f 35 0d 66 0f ab', - 3056: 'af ec c0 37 fd b7 b0 83 8e b3 d7 0b cd 26 83 82', - 3072: 'db c1 a7 b4 9d 57 35 8c c9 fa 6d 61 d7 3b 7c f0', - 4080: '63 49 d1 26 a3 7a fc ba 89 79 4f 98 04 91 4f dc', - 4096: 'bf 42 c3 01 8c 2f 7c 66 bf de 52 49 75 76 81 15' - } - ), - ( - '1910833222772a', - { - 0: 'bc 92 22 db d3 27 4d 8f c6 6d 14 cc bd a6 69 0b', - 16: '7a e6 27 41 0c 9a 2b e6 93 df 5b b7 48 5a 63 e3', - 240: '3f 09 31 aa 03 de fb 30 0f 06 01 03 82 6f 2a 64', - 256: 'be aa 9e c8 d5 9b b6 81 29 f3 02 7c 96 36 11 81', - 496: '74 e0 4d b4 6d 28 64 8d 7d ee 8a 00 64 b0 6c fe', - 512: '9b 5e 81 c6 2f e0 23 c5 5b e4 2f 87 bb f9 32 b8', - 752: 'ce 17 8f c1 82 6e fe cb c1 82 f5 79 99 a4 61 40', - 768: '8b df 55 cd 55 06 1c 06 db a6 be 11 de 4a 57 8a', - 1008: '62 6f 5f 4d ce 65 25 01 f3 08 7d 39 c9 2c c3 49', - 1024: '42 da ac 6a 8f 9a b9 a7 fd 13 7c 60 37 82 56 82', - 1520: 'cc 03 fd b7 91 92 a2 07 31 2f 53 f5 d4 dc 33 d9', - 1536: 'f7 0f 14 12 2a 1c 98 a3 15 5d 28 b8 a0 a8 a4 1d', - 2032: '2a 3a 30 7a b2 70 8a 9c 00 fe 0b 42 f9 c2 d6 a1', - 2048: '86 26 17 62 7d 22 61 ea b0 b1 24 65 97 ca 0a e9', - 3056: '55 f8 77 ce 4f 2e 1d db bf 8e 13 e2 cd e0 fd c8', - 3072: '1b 15 56 cb 93 5f 17 33 37 70 5f bb 5d 50 1f c1', - 4080: 'ec d0 e9 66 02 be 7f 8d 50 92 81 6c cc f2 c2 e9', - 4096: '02 78 81 fa b4 99 3a 1c 26 20 24 a9 4f ff 3f 61' - } - ), - # Page 8 - ( - '641910833222772a', - { - 0: 'bb f6 09 de 94 13 17 2d 07 66 0c b6 80 71 69 26', - 16: '46 10 1a 6d ab 43 11 5d 6c 52 2b 4f e9 36 04 a9', - 240: 'cb e1 ff f2 1c 96 f3 ee f6 1e 8f e0 54 2c bd f0', - 256: '34 79 38 bf fa 40 09 c5 12 cf b4 03 4b 0d d1 a7', - 496: '78 67 a7 86 d0 0a 71 47 90 4d 76 dd f1 e5 20 e3', - 512: '8d 3e 9e 1c ae fc cc b3 fb f8 d1 8f 64 12 0b 32', - 752: '94 23 37 f8 fd 76 f0 fa e8 c5 2d 79 54 81 06 72', - 768: 'b8 54 8c 10 f5 16 67 f6 e6 0e 18 2f a1 9b 30 f7', - 1008: '02 11 c7 c6 19 0c 9e fd 12 37 c3 4c 8f 2e 06 c4', - 1024: 'bd a6 4f 65 27 6d 2a ac b8 f9 02 12 20 3a 80 8e', - 1520: 'bd 38 20 f7 32 ff b5 3e c1 93 e7 9d 33 e2 7c 73', - 1536: 'd0 16 86 16 86 19 07 d4 82 e3 6c da c8 cf 57 49', - 2032: '97 b0 f0 f2 24 b2 d2 31 71 14 80 8f b0 3a f7 a0', - 2048: 'e5 96 16 e4 69 78 79 39 a0 63 ce ea 9a f9 56 d1', - 3056: 'c4 7e 0d c1 66 09 19 c1 11 01 20 8f 9e 69 aa 1f', - 3072: '5a e4 f1 28 96 b8 37 9a 2a ad 89 b5 b5 53 d6 b0', - 4080: '6b 6b 09 8d 0c 29 3b c2 99 3d 80 bf 05 18 b6 d9', - 4096: '81 70 cc 3c cd 92 a6 98 62 1b 93 9d d3 8f e7 b9' - } - ), - ( - '8b37641910833222772a', - { - 0: 'ab 65 c2 6e dd b2 87 60 0d b2 fd a1 0d 1e 60 5c', - 16: 'bb 75 90 10 c2 96 58 f2 c7 2d 93 a2 d1 6d 29 30', - 240: 'b9 01 e8 03 6e d1 c3 83 cd 3c 4c 4d d0 a6 ab 05', - 256: '3d 25 ce 49 22 92 4c 55 f0 64 94 33 53 d7 8a 6c', - 496: '12 c1 aa 44 bb f8 7e 75 e6 11 f6 9b 2c 38 f4 9b', - 512: '28 f2 b3 43 4b 65 c0 98 77 47 00 44 c6 ea 17 0d', - 752: 'bd 9e f8 22 de 52 88 19 61 34 cf 8a f7 83 93 04', - 768: '67 55 9c 23 f0 52 15 84 70 a2 96 f7 25 73 5a 32', - 1008: '8b ab 26 fb c2 c1 2b 0f 13 e2 ab 18 5e ab f2 41', - 1024: '31 18 5a 6d 69 6f 0c fa 9b 42 80 8b 38 e1 32 a2', - 1520: '56 4d 3d ae 18 3c 52 34 c8 af 1e 51 06 1c 44 b5', - 1536: '3c 07 78 a7 b5 f7 2d 3c 23 a3 13 5c 7d 67 b9 f4', - 2032: 'f3 43 69 89 0f cf 16 fb 51 7d ca ae 44 63 b2 dd', - 2048: '02 f3 1c 81 e8 20 07 31 b8 99 b0 28 e7 91 bf a7', - 3056: '72 da 64 62 83 22 8c 14 30 08 53 70 17 95 61 6f', - 3072: '4e 0a 8c 6f 79 34 a7 88 e2 26 5e 81 d6 d0 c8 f4', - 4080: '43 8d d5 ea fe a0 11 1b 6f 36 b4 b9 38 da 2a 68', - 4096: '5f 6b fc 73 81 58 74 d9 71 00 f0 86 97 93 57 d8' - } - ), - # Page 9 - ( - 'ebb46227c6cc8b37641910833222772a', - { - 0: '72 0c 94 b6 3e df 44 e1 31 d9 50 ca 21 1a 5a 30', - 16: 'c3 66 fd ea cf 9c a8 04 36 be 7c 35 84 24 d2 0b', - 240: 'b3 39 4a 40 aa bf 75 cb a4 22 82 ef 25 a0 05 9f', - 256: '48 47 d8 1d a4 94 2d bc 24 9d ef c4 8c 92 2b 9f', - 496: '08 12 8c 46 9f 27 53 42 ad da 20 2b 2b 58 da 95', - 512: '97 0d ac ef 40 ad 98 72 3b ac 5d 69 55 b8 17 61', - 752: '3c b8 99 93 b0 7b 0c ed 93 de 13 d2 a1 10 13 ac', - 768: 'ef 2d 67 6f 15 45 c2 c1 3d c6 80 a0 2f 4a db fe', - 1008: 'b6 05 95 51 4f 24 bc 9f e5 22 a6 ca d7 39 36 44', - 1024: 'b5 15 a8 c5 01 17 54 f5 90 03 05 8b db 81 51 4e', - 1520: '3c 70 04 7e 8c bc 03 8e 3b 98 20 db 60 1d a4 95', - 1536: '11 75 da 6e e7 56 de 46 a5 3e 2b 07 56 60 b7 70', - 2032: '00 a5 42 bb a0 21 11 cc 2c 65 b3 8e bd ba 58 7e', - 2048: '58 65 fd bb 5b 48 06 41 04 e8 30 b3 80 f2 ae de', - 3056: '34 b2 1a d2 ad 44 e9 99 db 2d 7f 08 63 f0 d9 b6', - 3072: '84 a9 21 8f c3 6e 8a 5f 2c cf be ae 53 a2 7d 25', - 4080: 'a2 22 1a 11 b8 33 cc b4 98 a5 95 40 f0 54 5f 4a', - 4096: '5b be b4 78 7d 59 e5 37 3f db ea 6c 6f 75 c2 9b' - } - ), - ( - 'c109163908ebe51debb46227c6cc8b37641910833222772a', - { - 0: '54 b6 4e 6b 5a 20 b5 e2 ec 84 59 3d c7 98 9d a7', - 16: 'c1 35 ee e2 37 a8 54 65 ff 97 dc 03 92 4f 45 ce', - 240: 'cf cc 92 2f b4 a1 4a b4 5d 61 75 aa bb f2 d2 01', - 256: '83 7b 87 e2 a4 46 ad 0e f7 98 ac d0 2b 94 12 4f', - 496: '17 a6 db d6 64 92 6a 06 36 b3 f4 c3 7a 4f 46 94', - 512: '4a 5f 9f 26 ae ee d4 d4 a2 5f 63 2d 30 52 33 d9', - 752: '80 a3 d0 1e f0 0c 8e 9a 42 09 c1 7f 4e eb 35 8c', - 768: 'd1 5e 7d 5f fa aa bc 02 07 bf 20 0a 11 77 93 a2', - 1008: '34 96 82 bf 58 8e aa 52 d0 aa 15 60 34 6a ea fa', - 1024: 'f5 85 4c db 76 c8 89 e3 ad 63 35 4e 5f 72 75 e3', - 1520: '53 2c 7c ec cb 39 df 32 36 31 84 05 a4 b1 27 9c', - 1536: 'ba ef e6 d9 ce b6 51 84 22 60 e0 d1 e0 5e 3b 90', - 2032: 'e8 2d 8c 6d b5 4e 3c 63 3f 58 1c 95 2b a0 42 07', - 2048: '4b 16 e5 0a bd 38 1b d7 09 00 a9 cd 9a 62 cb 23', - 3056: '36 82 ee 33 bd 14 8b d9 f5 86 56 cd 8f 30 d9 fb', - 3072: '1e 5a 0b 84 75 04 5d 9b 20 b2 62 86 24 ed fd 9e', - 4080: '63 ed d6 84 fb 82 62 82 fe 52 8f 9c 0e 92 37 bc', - 4096: 'e4 dd 2e 98 d6 96 0f ae 0b 43 54 54 56 74 33 91' - } - ), - # Page 10 - ( - '1ada31d5cf688221c109163908ebe51debb46227c6cc8b37641910833222772a', - { - 0: 'dd 5b cb 00 18 e9 22 d4 94 75 9d 7c 39 5d 02 d3', - 16: 'c8 44 6f 8f 77 ab f7 37 68 53 53 eb 89 a1 c9 eb', - 240: 'af 3e 30 f9 c0 95 04 59 38 15 15 75 c3 fb 90 98', - 256: 'f8 cb 62 74 db 99 b8 0b 1d 20 12 a9 8e d4 8f 0e', - 496: '25 c3 00 5a 1c b8 5d e0 76 25 98 39 ab 71 98 ab', - 512: '9d cb c1 83 e8 cb 99 4b 72 7b 75 be 31 80 76 9c', - 752: 'a1 d3 07 8d fa 91 69 50 3e d9 d4 49 1d ee 4e b2', - 768: '85 14 a5 49 58 58 09 6f 59 6e 4b cd 66 b1 06 65', - 1008: '5f 40 d5 9e c1 b0 3b 33 73 8e fa 60 b2 25 5d 31', - 1024: '34 77 c7 f7 64 a4 1b ac ef f9 0b f1 4f 92 b7 cc', - 1520: 'ac 4e 95 36 8d 99 b9 eb 78 b8 da 8f 81 ff a7 95', - 1536: '8c 3c 13 f8 c2 38 8b b7 3f 38 57 6e 65 b7 c4 46', - 2032: '13 c4 b9 c1 df b6 65 79 ed dd 8a 28 0b 9f 73 16', - 2048: 'dd d2 78 20 55 01 26 69 8e fa ad c6 4b 64 f6 6e', - 3056: 'f0 8f 2e 66 d2 8e d1 43 f3 a2 37 cf 9d e7 35 59', - 3072: '9e a3 6c 52 55 31 b8 80 ba 12 43 34 f5 7b 0b 70', - 4080: 'd5 a3 9e 3d fc c5 02 80 ba c4 a6 b5 aa 0d ca 7d', - 4096: '37 0b 1c 1f e6 55 91 6d 97 fd 0d 47 ca 1d 72 b8' - } - ) - ] - - def test_keystream(self): - for tv in self.rfc6229_data: - key = unhexlify(b((tv[0]))) - cipher = ARC4.new(key) - count = 0 - for offset in range(0, 4096+1, 16): - ct = cipher.encrypt(b('\x00')*16) - expected = tv[1].get(offset) - if expected: - expected = unhexlify(b(expected.replace(" ", ''))) - self.assertEqual(ct, expected) - count += 1 - self.assertEqual(count, len(tv[1])) - - -class Drop_Tests(unittest.TestCase): - key = b('\xAA')*16 - data = b('\x00')*5000 - - def setUp(self): - self.cipher = ARC4.new(self.key) - - def test_drop256_encrypt(self): - cipher_drop = ARC4.new(self.key, 256) - ct_drop = cipher_drop.encrypt(self.data[:16]) - ct = self.cipher.encrypt(self.data)[256:256+16] - self.assertEqual(ct_drop, ct) - - def test_drop256_decrypt(self): - cipher_drop = ARC4.new(self.key, 256) - pt_drop = cipher_drop.decrypt(self.data[:16]) - pt = self.cipher.decrypt(self.data)[256:256+16] - self.assertEqual(pt_drop, pt) - - -class KeyLength(unittest.TestCase): - - def runTest(self): - self.assertRaises(ValueError, ARC4.new, b'') - self.assertRaises(ValueError, ARC4.new, b'\x00' * 257) - - -def get_tests(config={}): - from .common import make_stream_tests - tests = make_stream_tests(ARC4, "ARC4", test_data) - tests += list_test_cases(RFC6229_Tests) - tests += list_test_cases(Drop_Tests) - tests.append(KeyLength()) - return tests - - -if __name__ == '__main__': - def suite(): - return unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_Blowfish.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_Blowfish.py deleted file mode 100644 index ca5c603..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_Blowfish.py +++ /dev/null @@ -1,160 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Cipher/test_Blowfish.py: Self-test for the Blowfish cipher -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Cipher.Blowfish""" - -import unittest - -from Cryptodome.Util.py3compat import bchr - -from Cryptodome.Cipher import Blowfish - -# This is a list of (plaintext, ciphertext, key) tuples. -test_data = [ - # Test vectors from http://www.schneier.com/code/vectors.txt - ('0000000000000000', '4ef997456198dd78', '0000000000000000'), - ('ffffffffffffffff', '51866fd5b85ecb8a', 'ffffffffffffffff'), - ('1000000000000001', '7d856f9a613063f2', '3000000000000000'), - ('1111111111111111', '2466dd878b963c9d', '1111111111111111'), - ('1111111111111111', '61f9c3802281b096', '0123456789abcdef'), - ('0123456789abcdef', '7d0cc630afda1ec7', '1111111111111111'), - ('0000000000000000', '4ef997456198dd78', '0000000000000000'), - ('0123456789abcdef', '0aceab0fc6a0a28d', 'fedcba9876543210'), - ('01a1d6d039776742', '59c68245eb05282b', '7ca110454a1a6e57'), - ('5cd54ca83def57da', 'b1b8cc0b250f09a0', '0131d9619dc1376e'), - ('0248d43806f67172', '1730e5778bea1da4', '07a1133e4a0b2686'), - ('51454b582ddf440a', 'a25e7856cf2651eb', '3849674c2602319e'), - ('42fd443059577fa2', '353882b109ce8f1a', '04b915ba43feb5b6'), - ('059b5e0851cf143a', '48f4d0884c379918', '0113b970fd34f2ce'), - ('0756d8e0774761d2', '432193b78951fc98', '0170f175468fb5e6'), - ('762514b829bf486a', '13f04154d69d1ae5', '43297fad38e373fe'), - ('3bdd119049372802', '2eedda93ffd39c79', '07a7137045da2a16'), - ('26955f6835af609a', 'd887e0393c2da6e3', '04689104c2fd3b2f'), - ('164d5e404f275232', '5f99d04f5b163969', '37d06bb516cb7546'), - ('6b056e18759f5cca', '4a057a3b24d3977b', '1f08260d1ac2465e'), - ('004bd6ef09176062', '452031c1e4fada8e', '584023641aba6176'), - ('480d39006ee762f2', '7555ae39f59b87bd', '025816164629b007'), - ('437540c8698f3cfa', '53c55f9cb49fc019', '49793ebc79b3258f'), - ('072d43a077075292', '7a8e7bfa937e89a3', '4fb05e1515ab73a7'), - ('02fe55778117f12a', 'cf9c5d7a4986adb5', '49e95d6d4ca229bf'), - ('1d9d5c5018f728c2', 'd1abb290658bc778', '018310dc409b26d6'), - ('305532286d6f295a', '55cb3774d13ef201', '1c587f1c13924fef'), - ('0123456789abcdef', 'fa34ec4847b268b2', '0101010101010101'), - ('0123456789abcdef', 'a790795108ea3cae', '1f1f1f1f0e0e0e0e'), - ('0123456789abcdef', 'c39e072d9fac631d', 'e0fee0fef1fef1fe'), - ('ffffffffffffffff', '014933e0cdaff6e4', '0000000000000000'), - ('0000000000000000', 'f21e9a77b71c49bc', 'ffffffffffffffff'), - ('0000000000000000', '245946885754369a', '0123456789abcdef'), - ('ffffffffffffffff', '6b5c5a9c5d9e0a5a', 'fedcba9876543210'), - #('fedcba9876543210', 'f9ad597c49db005e', 'f0'), - #('fedcba9876543210', 'e91d21c1d961a6d6', 'f0e1'), - #('fedcba9876543210', 'e9c2b70a1bc65cf3', 'f0e1d2'), - ('fedcba9876543210', 'be1e639408640f05', 'f0e1d2c3'), - ('fedcba9876543210', 'b39e44481bdb1e6e', 'f0e1d2c3b4'), - ('fedcba9876543210', '9457aa83b1928c0d', 'f0e1d2c3b4a5'), - ('fedcba9876543210', '8bb77032f960629d', 'f0e1d2c3b4a596'), - ('fedcba9876543210', 'e87a244e2cc85e82', 'f0e1d2c3b4a59687'), - ('fedcba9876543210', '15750e7a4f4ec577', 'f0e1d2c3b4a5968778'), - ('fedcba9876543210', '122ba70b3ab64ae0', 'f0e1d2c3b4a596877869'), - ('fedcba9876543210', '3a833c9affc537f6', 'f0e1d2c3b4a5968778695a'), - ('fedcba9876543210', '9409da87a90f6bf2', 'f0e1d2c3b4a5968778695a4b'), - ('fedcba9876543210', '884f80625060b8b4', 'f0e1d2c3b4a5968778695a4b3c'), - ('fedcba9876543210', '1f85031c19e11968', 'f0e1d2c3b4a5968778695a4b3c2d'), - ('fedcba9876543210', '79d9373a714ca34f', 'f0e1d2c3b4a5968778695a4b3c2d1e'), - ('fedcba9876543210', '93142887ee3be15c', - 'f0e1d2c3b4a5968778695a4b3c2d1e0f'), - ('fedcba9876543210', '03429e838ce2d14b', - 'f0e1d2c3b4a5968778695a4b3c2d1e0f00'), - ('fedcba9876543210', 'a4299e27469ff67b', - 'f0e1d2c3b4a5968778695a4b3c2d1e0f0011'), - ('fedcba9876543210', 'afd5aed1c1bc96a8', - 'f0e1d2c3b4a5968778695a4b3c2d1e0f001122'), - ('fedcba9876543210', '10851c0e3858da9f', - 'f0e1d2c3b4a5968778695a4b3c2d1e0f00112233'), - ('fedcba9876543210', 'e6f51ed79b9db21f', - 'f0e1d2c3b4a5968778695a4b3c2d1e0f0011223344'), - ('fedcba9876543210', '64a6e14afd36b46f', - 'f0e1d2c3b4a5968778695a4b3c2d1e0f001122334455'), - ('fedcba9876543210', '80c7d7d45a5479ad', - 'f0e1d2c3b4a5968778695a4b3c2d1e0f00112233445566'), - ('fedcba9876543210', '05044b62fa52d080', - 'f0e1d2c3b4a5968778695a4b3c2d1e0f0011223344556677'), -] - - -class KeyLength(unittest.TestCase): - - def runTest(self): - self.assertRaises(ValueError, Blowfish.new, bchr(0) * 3, - Blowfish.MODE_ECB) - self.assertRaises(ValueError, Blowfish.new, bchr(0) * 57, - Blowfish.MODE_ECB) - - -class TestOutput(unittest.TestCase): - - def runTest(self): - # Encrypt/Decrypt data and test output parameter - - cipher = Blowfish.new(b'4'*16, Blowfish.MODE_ECB) - - pt = b'5' * 16 - ct = cipher.encrypt(pt) - - output = bytearray(16) - res = cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - self.assertEqual(res, None) - - res = cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - self.assertEqual(res, None) - - output = memoryview(bytearray(16)) - cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - - cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - - self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0'*16) - self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0'*16) - - shorter_output = bytearray(7) - self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output) - self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output) - - -def get_tests(config={}): - from .common import make_block_tests - tests = make_block_tests(Blowfish, "Blowfish", test_data) - tests.append(KeyLength()) - tests += [TestOutput()] - return tests - - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CAST.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CAST.py deleted file mode 100644 index 8bc21fd..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CAST.py +++ /dev/null @@ -1,101 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Cipher/CAST.py: Self-test for the CAST-128 (CAST5) cipher -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Cipher.CAST""" - -import unittest - -from Cryptodome.Util.py3compat import bchr - -from Cryptodome.Cipher import CAST - -# This is a list of (plaintext, ciphertext, key) tuples. -test_data = [ - # Test vectors from RFC 2144, B.1 - ('0123456789abcdef', '238b4fe5847e44b2', - '0123456712345678234567893456789a', - '128-bit key'), - - ('0123456789abcdef', 'eb6a711a2c02271b', - '01234567123456782345', - '80-bit key'), - - ('0123456789abcdef', '7ac816d16e9b302e', - '0123456712', - '40-bit key'), -] - - -class KeyLength(unittest.TestCase): - - def runTest(self): - self.assertRaises(ValueError, CAST.new, bchr(0) * 4, CAST.MODE_ECB) - self.assertRaises(ValueError, CAST.new, bchr(0) * 17, CAST.MODE_ECB) - - -class TestOutput(unittest.TestCase): - - def runTest(self): - # Encrypt/Decrypt data and test output parameter - - cipher = CAST.new(b'4'*16, CAST.MODE_ECB) - - pt = b'5' * 16 - ct = cipher.encrypt(pt) - - output = bytearray(16) - res = cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - self.assertEqual(res, None) - - res = cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - self.assertEqual(res, None) - - output = memoryview(bytearray(16)) - cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - - cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - - self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0'*16) - self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0'*16) - - shorter_output = bytearray(7) - self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output) - self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output) - - -def get_tests(config={}): - from .common import make_block_tests - - tests = make_block_tests(CAST, "CAST", test_data) - tests.append(KeyLength()) - tests.append(TestOutput()) - return tests - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CBC.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CBC.py deleted file mode 100644 index f118eb6..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CBC.py +++ /dev/null @@ -1,556 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import unittest -from binascii import unhexlify - -from Cryptodome.SelfTest.loader import load_test_vectors -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.Util.py3compat import tobytes, is_string -from Cryptodome.Cipher import AES, DES3, DES -from Cryptodome.Hash import SHAKE128 - - -def get_tag_random(tag, length): - return SHAKE128.new(data=tobytes(tag)).read(length) - -class BlockChainingTests(unittest.TestCase): - - key_128 = get_tag_random("key_128", 16) - key_192 = get_tag_random("key_192", 24) - iv_128 = get_tag_random("iv_128", 16) - iv_64 = get_tag_random("iv_64", 8) - data_128 = get_tag_random("data_128", 16) - - def test_loopback_128(self): - cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) - pt = get_tag_random("plaintext", 16 * 100) - ct = cipher.encrypt(pt) - - cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) - pt2 = cipher.decrypt(ct) - self.assertEqual(pt, pt2) - - def test_loopback_64(self): - cipher = DES3.new(self.key_192, self.des3_mode, self.iv_64) - pt = get_tag_random("plaintext", 8 * 100) - ct = cipher.encrypt(pt) - - cipher = DES3.new(self.key_192, self.des3_mode, self.iv_64) - pt2 = cipher.decrypt(ct) - self.assertEqual(pt, pt2) - - def test_iv(self): - # If not passed, the iv is created randomly - cipher = AES.new(self.key_128, self.aes_mode) - iv1 = cipher.iv - cipher = AES.new(self.key_128, self.aes_mode) - iv2 = cipher.iv - self.assertNotEqual(iv1, iv2) - self.assertEqual(len(iv1), 16) - - # IV can be passed in uppercase or lowercase - cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) - ct = cipher.encrypt(self.data_128) - - cipher = AES.new(self.key_128, self.aes_mode, iv=self.iv_128) - self.assertEqual(ct, cipher.encrypt(self.data_128)) - - cipher = AES.new(self.key_128, self.aes_mode, IV=self.iv_128) - self.assertEqual(ct, cipher.encrypt(self.data_128)) - - def test_iv_must_be_bytes(self): - self.assertRaises(TypeError, AES.new, self.key_128, self.aes_mode, - iv = u'test1234567890-*') - - def test_only_one_iv(self): - # Only one IV/iv keyword allowed - self.assertRaises(TypeError, AES.new, self.key_128, self.aes_mode, - iv=self.iv_128, IV=self.iv_128) - - def test_iv_with_matching_length(self): - self.assertRaises(ValueError, AES.new, self.key_128, self.aes_mode, - b"") - self.assertRaises(ValueError, AES.new, self.key_128, self.aes_mode, - self.iv_128[:15]) - self.assertRaises(ValueError, AES.new, self.key_128, self.aes_mode, - self.iv_128 + b"0") - - def test_block_size_128(self): - cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) - self.assertEqual(cipher.block_size, AES.block_size) - - def test_block_size_64(self): - cipher = DES3.new(self.key_192, self.des3_mode, self.iv_64) - self.assertEqual(cipher.block_size, DES3.block_size) - - def test_unaligned_data_128(self): - cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) - for wrong_length in range(1,16): - self.assertRaises(ValueError, cipher.encrypt, b"5" * wrong_length) - - cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) - for wrong_length in range(1,16): - self.assertRaises(ValueError, cipher.decrypt, b"5" * wrong_length) - - def test_unaligned_data_64(self): - cipher = DES3.new(self.key_192, self.des3_mode, self.iv_64) - for wrong_length in range(1,8): - self.assertRaises(ValueError, cipher.encrypt, b"5" * wrong_length) - - cipher = DES3.new(self.key_192, self.des3_mode, self.iv_64) - for wrong_length in range(1,8): - self.assertRaises(ValueError, cipher.decrypt, b"5" * wrong_length) - - def test_IV_iv_attributes(self): - data = get_tag_random("data", 16 * 100) - for func in "encrypt", "decrypt": - cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) - getattr(cipher, func)(data) - self.assertEqual(cipher.iv, self.iv_128) - self.assertEqual(cipher.IV, self.iv_128) - - def test_unknown_parameters(self): - self.assertRaises(TypeError, AES.new, self.key_128, self.aes_mode, - self.iv_128, 7) - self.assertRaises(TypeError, AES.new, self.key_128, self.aes_mode, - iv=self.iv_128, unknown=7) - # But some are only known by the base cipher (e.g. use_aesni consumed by the AES module) - AES.new(self.key_128, self.aes_mode, iv=self.iv_128, use_aesni=False) - - def test_null_encryption_decryption(self): - for func in "encrypt", "decrypt": - cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) - result = getattr(cipher, func)(b"") - self.assertEqual(result, b"") - - def test_either_encrypt_or_decrypt(self): - cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) - cipher.encrypt(b"") - self.assertRaises(TypeError, cipher.decrypt, b"") - - cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) - cipher.decrypt(b"") - self.assertRaises(TypeError, cipher.encrypt, b"") - - def test_data_must_be_bytes(self): - cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) - self.assertRaises(TypeError, cipher.encrypt, u'test1234567890-*') - - cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) - self.assertRaises(TypeError, cipher.decrypt, u'test1234567890-*') - - def test_bytearray(self): - data = b"1" * 128 - data_ba = bytearray(data) - - # Encrypt - key_ba = bytearray(self.key_128) - iv_ba = bytearray(self.iv_128) - - cipher1 = AES.new(self.key_128, self.aes_mode, self.iv_128) - ref1 = cipher1.encrypt(data) - - cipher2 = AES.new(key_ba, self.aes_mode, iv_ba) - key_ba[:3] = b'\xFF\xFF\xFF' - iv_ba[:3] = b'\xFF\xFF\xFF' - ref2 = cipher2.encrypt(data_ba) - - self.assertEqual(ref1, ref2) - self.assertEqual(cipher1.iv, cipher2.iv) - - # Decrypt - key_ba = bytearray(self.key_128) - iv_ba = bytearray(self.iv_128) - - cipher3 = AES.new(self.key_128, self.aes_mode, self.iv_128) - ref3 = cipher3.decrypt(data) - - cipher4 = AES.new(key_ba, self.aes_mode, iv_ba) - key_ba[:3] = b'\xFF\xFF\xFF' - iv_ba[:3] = b'\xFF\xFF\xFF' - ref4 = cipher4.decrypt(data_ba) - - self.assertEqual(ref3, ref4) - - def test_memoryview(self): - data = b"1" * 128 - data_mv = memoryview(bytearray(data)) - - # Encrypt - key_mv = memoryview(bytearray(self.key_128)) - iv_mv = memoryview(bytearray(self.iv_128)) - - cipher1 = AES.new(self.key_128, self.aes_mode, self.iv_128) - ref1 = cipher1.encrypt(data) - - cipher2 = AES.new(key_mv, self.aes_mode, iv_mv) - key_mv[:3] = b'\xFF\xFF\xFF' - iv_mv[:3] = b'\xFF\xFF\xFF' - ref2 = cipher2.encrypt(data_mv) - - self.assertEqual(ref1, ref2) - self.assertEqual(cipher1.iv, cipher2.iv) - - # Decrypt - key_mv = memoryview(bytearray(self.key_128)) - iv_mv = memoryview(bytearray(self.iv_128)) - - cipher3 = AES.new(self.key_128, self.aes_mode, self.iv_128) - ref3 = cipher3.decrypt(data) - - cipher4 = AES.new(key_mv, self.aes_mode, iv_mv) - key_mv[:3] = b'\xFF\xFF\xFF' - iv_mv[:3] = b'\xFF\xFF\xFF' - ref4 = cipher4.decrypt(data_mv) - - self.assertEqual(ref3, ref4) - - def test_output_param(self): - - pt = b'5' * 128 - cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128) - ct = cipher.encrypt(pt) - - output = bytearray(128) - cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128) - res = cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - self.assertEqual(res, None) - - cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128) - res = cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - self.assertEqual(res, None) - - - def test_output_param_same_buffer(self): - - pt = b'5' * 128 - cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128) - ct = cipher.encrypt(pt) - - pt_ba = bytearray(pt) - cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128) - res = cipher.encrypt(pt_ba, output=pt_ba) - self.assertEqual(ct, pt_ba) - self.assertEqual(res, None) - - ct_ba = bytearray(ct) - cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128) - res = cipher.decrypt(ct_ba, output=ct_ba) - self.assertEqual(pt, ct_ba) - self.assertEqual(res, None) - - - def test_output_param_memoryview(self): - - pt = b'5' * 128 - cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128) - ct = cipher.encrypt(pt) - - output = memoryview(bytearray(128)) - cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128) - cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - - cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128) - cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - - def test_output_param_neg(self): - LEN_PT = 128 - - pt = b'5' * LEN_PT - cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128) - ct = cipher.encrypt(pt) - - cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128) - self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0' * LEN_PT) - - cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128) - self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0' * LEN_PT) - - shorter_output = bytearray(LEN_PT - 1) - cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128) - self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output) - cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128) - self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output) - - -class CbcTests(BlockChainingTests): - aes_mode = AES.MODE_CBC - des3_mode = DES3.MODE_CBC - - -class NistBlockChainingVectors(unittest.TestCase): - - def _do_kat_aes_test(self, file_name): - - test_vectors = load_test_vectors(("Cipher", "AES"), - file_name, - "AES CBC KAT", - { "count" : lambda x: int(x) } ) - if test_vectors is None: - return - - direction = None - for tv in test_vectors: - - # The test vector file contains some directive lines - if is_string(tv): - direction = tv - continue - - self.description = tv.desc - - cipher = AES.new(tv.key, self.aes_mode, tv.iv) - if direction == "[ENCRYPT]": - self.assertEqual(cipher.encrypt(tv.plaintext), tv.ciphertext) - elif direction == "[DECRYPT]": - self.assertEqual(cipher.decrypt(tv.ciphertext), tv.plaintext) - else: - assert False - - # See Section 6.4.2 in AESAVS - def _do_mct_aes_test(self, file_name): - - test_vectors = load_test_vectors(("Cipher", "AES"), - file_name, - "AES CBC Montecarlo", - { "count" : lambda x: int(x) } ) - if test_vectors is None: - return - - direction = None - for tv in test_vectors: - - # The test vector file contains some directive lines - if is_string(tv): - direction = tv - continue - - self.description = tv.desc - cipher = AES.new(tv.key, self.aes_mode, tv.iv) - - if direction == '[ENCRYPT]': - cts = [ tv.iv ] - for count in range(1000): - cts.append(cipher.encrypt(tv.plaintext)) - tv.plaintext = cts[-2] - self.assertEqual(cts[-1], tv.ciphertext) - elif direction == '[DECRYPT]': - pts = [ tv.iv] - for count in range(1000): - pts.append(cipher.decrypt(tv.ciphertext)) - tv.ciphertext = pts[-2] - self.assertEqual(pts[-1], tv.plaintext) - else: - assert False - - def _do_tdes_test(self, file_name): - - test_vectors = load_test_vectors(("Cipher", "TDES"), - file_name, - "TDES CBC KAT", - { "count" : lambda x: int(x) } ) - if test_vectors is None: - return - - direction = None - for tv in test_vectors: - - # The test vector file contains some directive lines - if is_string(tv): - direction = tv - continue - - self.description = tv.desc - if hasattr(tv, "keys"): - cipher = DES.new(tv.keys, self.des_mode, tv.iv) - else: - if tv.key1 != tv.key3: - key = tv.key1 + tv.key2 + tv.key3 # Option 3 - else: - key = tv.key1 + tv.key2 # Option 2 - cipher = DES3.new(key, self.des3_mode, tv.iv) - - if direction == "[ENCRYPT]": - self.assertEqual(cipher.encrypt(tv.plaintext), tv.ciphertext) - elif direction == "[DECRYPT]": - self.assertEqual(cipher.decrypt(tv.ciphertext), tv.plaintext) - else: - assert False - - -class NistCbcVectors(NistBlockChainingVectors): - aes_mode = AES.MODE_CBC - des_mode = DES.MODE_CBC - des3_mode = DES3.MODE_CBC - - -# Create one test method per file -nist_aes_kat_mmt_files = ( - # KAT - "CBCGFSbox128.rsp", - "CBCGFSbox192.rsp", - "CBCGFSbox256.rsp", - "CBCKeySbox128.rsp", - "CBCKeySbox192.rsp", - "CBCKeySbox256.rsp", - "CBCVarKey128.rsp", - "CBCVarKey192.rsp", - "CBCVarKey256.rsp", - "CBCVarTxt128.rsp", - "CBCVarTxt192.rsp", - "CBCVarTxt256.rsp", - # MMT - "CBCMMT128.rsp", - "CBCMMT192.rsp", - "CBCMMT256.rsp", - ) -nist_aes_mct_files = ( - "CBCMCT128.rsp", - "CBCMCT192.rsp", - "CBCMCT256.rsp", - ) - -for file_name in nist_aes_kat_mmt_files: - def new_func(self, file_name=file_name): - self._do_kat_aes_test(file_name) - setattr(NistCbcVectors, "test_AES_" + file_name, new_func) - -for file_name in nist_aes_mct_files: - def new_func(self, file_name=file_name): - self._do_mct_aes_test(file_name) - setattr(NistCbcVectors, "test_AES_" + file_name, new_func) -del file_name, new_func - -nist_tdes_files = ( - "TCBCMMT2.rsp", # 2TDES - "TCBCMMT3.rsp", # 3TDES - "TCBCinvperm.rsp", # Single DES - "TCBCpermop.rsp", - "TCBCsubtab.rsp", - "TCBCvarkey.rsp", - "TCBCvartext.rsp", - ) - -for file_name in nist_tdes_files: - def new_func(self, file_name=file_name): - self._do_tdes_test(file_name) - setattr(NistCbcVectors, "test_TDES_" + file_name, new_func) - -# END OF NIST CBC TEST VECTORS - - -class SP800TestVectors(unittest.TestCase): - """Class exercising the CBC test vectors found in Section F.2 - of NIST SP 800-3A""" - - def test_aes_128(self): - key = '2b7e151628aed2a6abf7158809cf4f3c' - iv = '000102030405060708090a0b0c0d0e0f' - plaintext = '6bc1bee22e409f96e93d7e117393172a' +\ - 'ae2d8a571e03ac9c9eb76fac45af8e51' +\ - '30c81c46a35ce411e5fbc1191a0a52ef' +\ - 'f69f2445df4f9b17ad2b417be66c3710' - ciphertext = '7649abac8119b246cee98e9b12e9197d' +\ - '5086cb9b507219ee95db113a917678b2' +\ - '73bed6b8e3c1743b7116e69e22229516' +\ - '3ff1caa1681fac09120eca307586e1a7' - - key = unhexlify(key) - iv = unhexlify(iv) - plaintext = unhexlify(plaintext) - ciphertext = unhexlify(ciphertext) - - cipher = AES.new(key, AES.MODE_CBC, iv) - self.assertEqual(cipher.encrypt(plaintext), ciphertext) - cipher = AES.new(key, AES.MODE_CBC, iv) - self.assertEqual(cipher.decrypt(ciphertext), plaintext) - - def test_aes_192(self): - key = '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b' - iv = '000102030405060708090a0b0c0d0e0f' - plaintext = '6bc1bee22e409f96e93d7e117393172a' +\ - 'ae2d8a571e03ac9c9eb76fac45af8e51' +\ - '30c81c46a35ce411e5fbc1191a0a52ef' +\ - 'f69f2445df4f9b17ad2b417be66c3710' - ciphertext = '4f021db243bc633d7178183a9fa071e8' +\ - 'b4d9ada9ad7dedf4e5e738763f69145a' +\ - '571b242012fb7ae07fa9baac3df102e0' +\ - '08b0e27988598881d920a9e64f5615cd' - - key = unhexlify(key) - iv = unhexlify(iv) - plaintext = unhexlify(plaintext) - ciphertext = unhexlify(ciphertext) - - cipher = AES.new(key, AES.MODE_CBC, iv) - self.assertEqual(cipher.encrypt(plaintext), ciphertext) - cipher = AES.new(key, AES.MODE_CBC, iv) - self.assertEqual(cipher.decrypt(ciphertext), plaintext) - - def test_aes_256(self): - key = '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4' - iv = '000102030405060708090a0b0c0d0e0f' - plaintext = '6bc1bee22e409f96e93d7e117393172a' +\ - 'ae2d8a571e03ac9c9eb76fac45af8e51' +\ - '30c81c46a35ce411e5fbc1191a0a52ef' +\ - 'f69f2445df4f9b17ad2b417be66c3710' - ciphertext = 'f58c4c04d6e5f1ba779eabfb5f7bfbd6' +\ - '9cfc4e967edb808d679f777bc6702c7d' +\ - '39f23369a9d9bacfa530e26304231461' +\ - 'b2eb05e2c39be9fcda6c19078c6a9d1b' - - key = unhexlify(key) - iv = unhexlify(iv) - plaintext = unhexlify(plaintext) - ciphertext = unhexlify(ciphertext) - - cipher = AES.new(key, AES.MODE_CBC, iv) - self.assertEqual(cipher.encrypt(plaintext), ciphertext) - cipher = AES.new(key, AES.MODE_CBC, iv) - self.assertEqual(cipher.decrypt(ciphertext), plaintext) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(CbcTests) - if config.get('slow_tests'): - tests += list_test_cases(NistCbcVectors) - tests += list_test_cases(SP800TestVectors) - return tests - - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CCM.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CCM.py deleted file mode 100644 index 2615720..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CCM.py +++ /dev/null @@ -1,936 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2015, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import unittest -from binascii import unhexlify - -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.SelfTest.loader import load_test_vectors_wycheproof -from Cryptodome.Util.py3compat import tobytes, bchr -from Cryptodome.Cipher import AES -from Cryptodome.Hash import SHAKE128 - -from Cryptodome.Util.strxor import strxor - - -def get_tag_random(tag, length): - return SHAKE128.new(data=tobytes(tag)).read(length) - - -class CcmTests(unittest.TestCase): - - key_128 = get_tag_random("key_128", 16) - nonce_96 = get_tag_random("nonce_128", 12) - data = get_tag_random("data", 128) - - def test_loopback_128(self): - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - pt = get_tag_random("plaintext", 16 * 100) - ct = cipher.encrypt(pt) - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - pt2 = cipher.decrypt(ct) - self.assertEqual(pt, pt2) - - def test_nonce(self): - # If not passed, the nonce is created randomly - cipher = AES.new(self.key_128, AES.MODE_CCM) - nonce1 = cipher.nonce - cipher = AES.new(self.key_128, AES.MODE_CCM) - nonce2 = cipher.nonce - self.assertEqual(len(nonce1), 11) - self.assertNotEqual(nonce1, nonce2) - - cipher = AES.new(self.key_128, AES.MODE_CCM, self.nonce_96) - ct = cipher.encrypt(self.data) - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - self.assertEqual(ct, cipher.encrypt(self.data)) - - def test_nonce_must_be_bytes(self): - self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CCM, - nonce=u'test12345678') - - def test_nonce_length(self): - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CCM, - nonce=b"") - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CCM, - nonce=bchr(1) * 6) - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CCM, - nonce=bchr(1) * 14) - for x in range(7, 13 + 1): - AES.new(self.key_128, AES.MODE_CCM, nonce=bchr(1) * x) - - def test_block_size(self): - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - self.assertEqual(cipher.block_size, AES.block_size) - - def test_nonce_attribute(self): - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - self.assertEqual(cipher.nonce, self.nonce_96) - - # By default, a 11 bytes long nonce is randomly generated - nonce1 = AES.new(self.key_128, AES.MODE_CCM).nonce - nonce2 = AES.new(self.key_128, AES.MODE_CCM).nonce - self.assertEqual(len(nonce1), 11) - self.assertNotEqual(nonce1, nonce2) - - def test_unknown_parameters(self): - self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CCM, - self.nonce_96, 7) - self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CCM, - nonce=self.nonce_96, unknown=7) - - # But some are only known by the base cipher - # (e.g. use_aesni consumed by the AES module) - AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96, - use_aesni=False) - - def test_null_encryption_decryption(self): - for func in "encrypt", "decrypt": - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - result = getattr(cipher, func)(b"") - self.assertEqual(result, b"") - - def test_either_encrypt_or_decrypt(self): - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - cipher.encrypt(b"") - self.assertRaises(TypeError, cipher.decrypt, b"") - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - cipher.decrypt(b"") - self.assertRaises(TypeError, cipher.encrypt, b"") - - def test_data_must_be_bytes(self): - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.encrypt, u'test1234567890-*') - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.decrypt, u'test1234567890-*') - - def test_mac_len(self): - # Invalid MAC length - for mac_len in range(3, 17 + 1, 2): - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CCM, - nonce=self.nonce_96, mac_len=mac_len) - - # Valid MAC length - for mac_len in range(4, 16 + 1, 2): - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96, - mac_len=mac_len) - _, mac = cipher.encrypt_and_digest(self.data) - self.assertEqual(len(mac), mac_len) - - # Default MAC length - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - _, mac = cipher.encrypt_and_digest(self.data) - self.assertEqual(len(mac), 16) - - def test_invalid_mac(self): - from Cryptodome.Util.strxor import strxor_c - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - ct, mac = cipher.encrypt_and_digest(self.data) - - invalid_mac = strxor_c(mac, 0x01) - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - self.assertRaises(ValueError, cipher.decrypt_and_verify, ct, - invalid_mac) - - def test_hex_mac(self): - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - mac_hex = cipher.hexdigest() - self.assertEqual(cipher.digest(), unhexlify(mac_hex)) - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - cipher.hexverify(mac_hex) - - def test_longer_assoc_data_than_declared(self): - # More than zero - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96, - assoc_len=0) - self.assertRaises(ValueError, cipher.update, b"1") - - # Too large - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96, - assoc_len=15) - self.assertRaises(ValueError, cipher.update, self.data) - - def test_shorter_assoc_data_than_expected(self): - DATA_LEN = len(self.data) - - # With plaintext - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96, - assoc_len=DATA_LEN + 1) - cipher.update(self.data) - self.assertRaises(ValueError, cipher.encrypt, self.data) - - # With empty plaintext - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96, - assoc_len=DATA_LEN + 1) - cipher.update(self.data) - self.assertRaises(ValueError, cipher.digest) - - # With ciphertext - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96, - assoc_len=DATA_LEN + 1) - cipher.update(self.data) - self.assertRaises(ValueError, cipher.decrypt, self.data) - - # With empty ciphertext - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - cipher.update(self.data) - mac = cipher.digest() - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96, - assoc_len=DATA_LEN + 1) - cipher.update(self.data) - self.assertRaises(ValueError, cipher.verify, mac) - - def test_shorter_and_longer_plaintext_than_declared(self): - DATA_LEN = len(self.data) - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96, - msg_len=DATA_LEN + 1) - cipher.encrypt(self.data) - self.assertRaises(ValueError, cipher.digest) - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96, - msg_len=DATA_LEN - 1) - self.assertRaises(ValueError, cipher.encrypt, self.data) - - def test_shorter_ciphertext_than_declared(self): - DATA_LEN = len(self.data) - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - ct, mac = cipher.encrypt_and_digest(self.data) - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96, - msg_len=DATA_LEN + 1) - cipher.decrypt(ct) - self.assertRaises(ValueError, cipher.verify, mac) - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96, - msg_len=DATA_LEN - 1) - self.assertRaises(ValueError, cipher.decrypt, ct) - - def test_message_chunks(self): - # Validate that both associated data and plaintext/ciphertext - # can be broken up in chunks of arbitrary length - - auth_data = get_tag_random("authenticated data", 127) - plaintext = get_tag_random("plaintext", 127) - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - cipher.update(auth_data) - ciphertext, ref_mac = cipher.encrypt_and_digest(plaintext) - - def break_up(data, chunk_length): - return [data[i:i+chunk_length] for i in range(0, len(data), - chunk_length)] - - # Encryption - for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128: - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96, - msg_len=127, assoc_len=127) - - for chunk in break_up(auth_data, chunk_length): - cipher.update(chunk) - pt2 = b"" - for chunk in break_up(ciphertext, chunk_length): - pt2 += cipher.decrypt(chunk) - self.assertEqual(plaintext, pt2) - cipher.verify(ref_mac) - - # Decryption - for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128: - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96, - msg_len=127, assoc_len=127) - - for chunk in break_up(auth_data, chunk_length): - cipher.update(chunk) - ct2 = b"" - for chunk in break_up(plaintext, chunk_length): - ct2 += cipher.encrypt(chunk) - self.assertEqual(ciphertext, ct2) - self.assertEqual(cipher.digest(), ref_mac) - - def test_bytearray(self): - - # Encrypt - key_ba = bytearray(self.key_128) - nonce_ba = bytearray(self.nonce_96) - header_ba = bytearray(self.data) - data_ba = bytearray(self.data) - - cipher1 = AES.new(self.key_128, - AES.MODE_CCM, - nonce=self.nonce_96) - cipher1.update(self.data) - ct = cipher1.encrypt(self.data) - tag = cipher1.digest() - - cipher2 = AES.new(key_ba, - AES.MODE_CCM, - nonce=nonce_ba) - key_ba[:3] = b"\xFF\xFF\xFF" - nonce_ba[:3] = b"\xFF\xFF\xFF" - cipher2.update(header_ba) - header_ba[:3] = b"\xFF\xFF\xFF" - ct_test = cipher2.encrypt(data_ba) - data_ba[:3] = b"\xFF\xFF\xFF" - tag_test = cipher2.digest() - - self.assertEqual(ct, ct_test) - self.assertEqual(tag, tag_test) - self.assertEqual(cipher1.nonce, cipher2.nonce) - - # Decrypt - key_ba = bytearray(self.key_128) - nonce_ba = bytearray(self.nonce_96) - header_ba = bytearray(self.data) - del data_ba - - cipher4 = AES.new(key_ba, - AES.MODE_CCM, - nonce=nonce_ba) - key_ba[:3] = b"\xFF\xFF\xFF" - nonce_ba[:3] = b"\xFF\xFF\xFF" - cipher4.update(header_ba) - header_ba[:3] = b"\xFF\xFF\xFF" - pt_test = cipher4.decrypt_and_verify(bytearray(ct_test), bytearray(tag_test)) - - self.assertEqual(self.data, pt_test) - - def test_memoryview(self): - - # Encrypt - key_mv = memoryview(bytearray(self.key_128)) - nonce_mv = memoryview(bytearray(self.nonce_96)) - header_mv = memoryview(bytearray(self.data)) - data_mv = memoryview(bytearray(self.data)) - - cipher1 = AES.new(self.key_128, - AES.MODE_CCM, - nonce=self.nonce_96) - cipher1.update(self.data) - ct = cipher1.encrypt(self.data) - tag = cipher1.digest() - - cipher2 = AES.new(key_mv, - AES.MODE_CCM, - nonce=nonce_mv) - key_mv[:3] = b"\xFF\xFF\xFF" - nonce_mv[:3] = b"\xFF\xFF\xFF" - cipher2.update(header_mv) - header_mv[:3] = b"\xFF\xFF\xFF" - ct_test = cipher2.encrypt(data_mv) - data_mv[:3] = b"\xFF\xFF\xFF" - tag_test = cipher2.digest() - - self.assertEqual(ct, ct_test) - self.assertEqual(tag, tag_test) - self.assertEqual(cipher1.nonce, cipher2.nonce) - - # Decrypt - key_mv = memoryview(bytearray(self.key_128)) - nonce_mv = memoryview(bytearray(self.nonce_96)) - header_mv = memoryview(bytearray(self.data)) - del data_mv - - cipher4 = AES.new(key_mv, - AES.MODE_CCM, - nonce=nonce_mv) - key_mv[:3] = b"\xFF\xFF\xFF" - nonce_mv[:3] = b"\xFF\xFF\xFF" - cipher4.update(header_mv) - header_mv[:3] = b"\xFF\xFF\xFF" - pt_test = cipher4.decrypt_and_verify(memoryview(ct_test), memoryview(tag_test)) - - self.assertEqual(self.data, pt_test) - - def test_output_param(self): - - pt = b'5' * 128 - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - ct = cipher.encrypt(pt) - tag = cipher.digest() - - output = bytearray(128) - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - res = cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - self.assertEqual(res, None) - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - res = cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - self.assertEqual(res, None) - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - res, tag_out = cipher.encrypt_and_digest(pt, output=output) - self.assertEqual(ct, output) - self.assertEqual(res, None) - self.assertEqual(tag, tag_out) - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - res = cipher.decrypt_and_verify(ct, tag, output=output) - self.assertEqual(pt, output) - self.assertEqual(res, None) - - def test_output_param_memoryview(self): - - pt = b'5' * 128 - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - ct = cipher.encrypt(pt) - - output = memoryview(bytearray(128)) - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - - def test_output_param_neg(self): - - pt = b'5' * 16 - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - ct = cipher.encrypt(pt) - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0'*16) - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0'*16) - - shorter_output = bytearray(15) - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output) - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output) - - -class CcmFSMTests(unittest.TestCase): - - key_128 = get_tag_random("key_128", 16) - nonce_96 = get_tag_random("nonce_128", 12) - data = get_tag_random("data", 16) - - def test_valid_init_encrypt_decrypt_digest_verify(self): - # No authenticated data, fixed plaintext - for assoc_len in (None, 0): - for msg_len in (None, len(self.data)): - # Verify path INIT->ENCRYPT->DIGEST - cipher = AES.new(self.key_128, AES.MODE_CCM, - nonce=self.nonce_96, - assoc_len=assoc_len, - msg_len=msg_len) - ct = cipher.encrypt(self.data) - mac = cipher.digest() - - # Verify path INIT->DECRYPT->VERIFY - cipher = AES.new(self.key_128, AES.MODE_CCM, - nonce=self.nonce_96, - assoc_len=assoc_len, - msg_len=msg_len) - cipher.decrypt(ct) - cipher.verify(mac) - - def test_valid_init_update_digest_verify(self): - # No plaintext, fixed authenticated data - for assoc_len in (None, len(self.data)): - for msg_len in (None, 0): - # Verify path INIT->UPDATE->DIGEST - cipher = AES.new(self.key_128, AES.MODE_CCM, - nonce=self.nonce_96, - assoc_len=assoc_len, - msg_len=msg_len) - cipher.update(self.data) - mac = cipher.digest() - - # Verify path INIT->UPDATE->VERIFY - cipher = AES.new(self.key_128, AES.MODE_CCM, - nonce=self.nonce_96, - assoc_len=assoc_len, - msg_len=msg_len) - cipher.update(self.data) - cipher.verify(mac) - - def test_valid_full_path(self): - # Fixed authenticated data, fixed plaintext - for assoc_len in (None, len(self.data)): - for msg_len in (None, len(self.data)): - # Verify path INIT->UPDATE->ENCRYPT->DIGEST - cipher = AES.new(self.key_128, AES.MODE_CCM, - nonce=self.nonce_96, - assoc_len=assoc_len, - msg_len=msg_len) - cipher.update(self.data) - ct = cipher.encrypt(self.data) - mac = cipher.digest() - - # Verify path INIT->UPDATE->DECRYPT->VERIFY - cipher = AES.new(self.key_128, AES.MODE_CCM, - nonce=self.nonce_96, - assoc_len=assoc_len, - msg_len=msg_len) - cipher.update(self.data) - cipher.decrypt(ct) - cipher.verify(mac) - - def test_valid_init_digest(self): - # Verify path INIT->DIGEST - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - cipher.digest() - - def test_valid_init_verify(self): - # Verify path INIT->VERIFY - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - mac = cipher.digest() - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - cipher.verify(mac) - - def test_valid_multiple_encrypt_or_decrypt(self): - # Only possible if msg_len is declared in advance - for method_name in "encrypt", "decrypt": - for auth_data in (None, b"333", self.data, - self.data + b"3"): - if auth_data is None: - assoc_len = None - else: - assoc_len = len(auth_data) - cipher = AES.new(self.key_128, AES.MODE_CCM, - nonce=self.nonce_96, - msg_len=64, - assoc_len=assoc_len) - if auth_data is not None: - cipher.update(auth_data) - method = getattr(cipher, method_name) - method(self.data) - method(self.data) - method(self.data) - method(self.data) - - def test_valid_multiple_digest_or_verify(self): - # Multiple calls to digest - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - cipher.update(self.data) - first_mac = cipher.digest() - for x in range(4): - self.assertEqual(first_mac, cipher.digest()) - - # Multiple calls to verify - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - cipher.update(self.data) - for x in range(5): - cipher.verify(first_mac) - - def test_valid_encrypt_and_digest_decrypt_and_verify(self): - # encrypt_and_digest - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - cipher.update(self.data) - ct, mac = cipher.encrypt_and_digest(self.data) - - # decrypt_and_verify - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - cipher.update(self.data) - pt = cipher.decrypt_and_verify(ct, mac) - self.assertEqual(self.data, pt) - - def test_invalid_multiple_encrypt_decrypt_without_msg_len(self): - # Once per method, with or without assoc. data - for method_name in "encrypt", "decrypt": - for assoc_data_present in (True, False): - cipher = AES.new(self.key_128, AES.MODE_CCM, - nonce=self.nonce_96) - if assoc_data_present: - cipher.update(self.data) - method = getattr(cipher, method_name) - method(self.data) - self.assertRaises(TypeError, method, self.data) - - def test_invalid_mixing_encrypt_decrypt(self): - # Once per method, with or without assoc. data - for method1_name, method2_name in (("encrypt", "decrypt"), - ("decrypt", "encrypt")): - for assoc_data_present in (True, False): - cipher = AES.new(self.key_128, AES.MODE_CCM, - nonce=self.nonce_96, - msg_len=32) - if assoc_data_present: - cipher.update(self.data) - getattr(cipher, method1_name)(self.data) - self.assertRaises(TypeError, getattr(cipher, method2_name), - self.data) - - def test_invalid_encrypt_or_update_after_digest(self): - for method_name in "encrypt", "update": - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - cipher.encrypt(self.data) - cipher.digest() - self.assertRaises(TypeError, getattr(cipher, method_name), - self.data) - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - cipher.encrypt_and_digest(self.data) - - def test_invalid_decrypt_or_update_after_verify(self): - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - ct = cipher.encrypt(self.data) - mac = cipher.digest() - - for method_name in "decrypt", "update": - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - cipher.decrypt(ct) - cipher.verify(mac) - self.assertRaises(TypeError, getattr(cipher, method_name), - self.data) - - cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) - cipher.decrypt_and_verify(ct, mac) - self.assertRaises(TypeError, getattr(cipher, method_name), - self.data) - - -class TestVectors(unittest.TestCase): - """Class exercising the CCM test vectors found in Appendix C - of NIST SP 800-38C and in RFC 3610""" - - # List of test vectors, each made up of: - # - authenticated data - # - plaintext - # - ciphertext - # - MAC - # - AES key - # - nonce - test_vectors_hex = [ - # NIST SP 800 38C - ( '0001020304050607', - '20212223', - '7162015b', - '4dac255d', - '404142434445464748494a4b4c4d4e4f', - '10111213141516'), - ( '000102030405060708090a0b0c0d0e0f', - '202122232425262728292a2b2c2d2e2f', - 'd2a1f0e051ea5f62081a7792073d593d', - '1fc64fbfaccd', - '404142434445464748494a4b4c4d4e4f', - '1011121314151617'), - ( '000102030405060708090a0b0c0d0e0f10111213', - '202122232425262728292a2b2c2d2e2f3031323334353637', - 'e3b201a9f5b71a7a9b1ceaeccd97e70b6176aad9a4428aa5', - '484392fbc1b09951', - '404142434445464748494a4b4c4d4e4f', - '101112131415161718191a1b'), - ( (''.join(["%02X" % (x*16+y) for x in range(0,16) for y in range(0,16)]))*256, - '202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f', - '69915dad1e84c6376a68c2967e4dab615ae0fd1faec44cc484828529463ccf72', - 'b4ac6bec93e8598e7f0dadbcea5b', - '404142434445464748494a4b4c4d4e4f', - '101112131415161718191a1b1c'), - # RFC3610 - ( '0001020304050607', - '08090a0b0c0d0e0f101112131415161718191a1b1c1d1e', - '588c979a61c663d2f066d0c2c0f989806d5f6b61dac384', - '17e8d12cfdf926e0', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - '00000003020100a0a1a2a3a4a5'), - ( - '0001020304050607', - '08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', - '72c91a36e135f8cf291ca894085c87e3cc15c439c9e43a3b', - 'a091d56e10400916', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - '00000004030201a0a1a2a3a4a5'), - ( '0001020304050607', - '08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20', - '51b1e5f44a197d1da46b0f8e2d282ae871e838bb64da859657', - '4adaa76fbd9fb0c5', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - '00000005040302A0A1A2A3A4A5'), - ( '000102030405060708090a0b', - '0c0d0e0f101112131415161718191a1b1c1d1e', - 'a28c6865939a9a79faaa5c4c2a9d4a91cdac8c', - '96c861b9c9e61ef1', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - '00000006050403a0a1a2a3a4a5'), - ( '000102030405060708090a0b', - '0c0d0e0f101112131415161718191a1b1c1d1e1f', - 'dcf1fb7b5d9e23fb9d4e131253658ad86ebdca3e', - '51e83f077d9c2d93', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - '00000007060504a0a1a2a3a4a5'), - ( '000102030405060708090a0b', - '0c0d0e0f101112131415161718191a1b1c1d1e1f20', - '6fc1b011f006568b5171a42d953d469b2570a4bd87', - '405a0443ac91cb94', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - '00000008070605a0a1a2a3a4a5'), - ( '0001020304050607', - '08090a0b0c0d0e0f101112131415161718191a1b1c1d1e', - '0135d1b2c95f41d5d1d4fec185d166b8094e999dfed96c', - '048c56602c97acbb7490', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - '00000009080706a0a1a2a3a4a5'), - ( '0001020304050607', - '08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', - '7b75399ac0831dd2f0bbd75879a2fd8f6cae6b6cd9b7db24', - 'c17b4433f434963f34b4', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - '0000000a090807a0a1a2a3a4a5'), - ( '0001020304050607', - '08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20', - '82531a60cc24945a4b8279181ab5c84df21ce7f9b73f42e197', - 'ea9c07e56b5eb17e5f4e', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - '0000000b0a0908a0a1a2a3a4a5'), - ( '000102030405060708090a0b', - '0c0d0e0f101112131415161718191a1b1c1d1e', - '07342594157785152b074098330abb141b947b', - '566aa9406b4d999988dd', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - '0000000c0b0a09a0a1a2a3a4a5'), - ( '000102030405060708090a0b', - '0c0d0e0f101112131415161718191a1b1c1d1e1f', - '676bb20380b0e301e8ab79590a396da78b834934', - 'f53aa2e9107a8b6c022c', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - '0000000d0c0b0aa0a1a2a3a4a5'), - ( '000102030405060708090a0b', - '0c0d0e0f101112131415161718191a1b1c1d1e1f20', - 'c0ffa0d6f05bdb67f24d43a4338d2aa4bed7b20e43', - 'cd1aa31662e7ad65d6db', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - '0000000e0d0c0ba0a1a2a3a4a5'), - ( '0be1a88bace018b1', - '08e8cf97d820ea258460e96ad9cf5289054d895ceac47c', - '4cb97f86a2a4689a877947ab8091ef5386a6ffbdd080f8', - 'e78cf7cb0cddd7b3', - 'd7828d13b2b0bdc325a76236df93cc6b', - '00412b4ea9cdbe3c9696766cfa'), - ( '63018f76dc8a1bcb', - '9020ea6f91bdd85afa0039ba4baff9bfb79c7028949cd0ec', - '4ccb1e7ca981befaa0726c55d378061298c85c92814abc33', - 'c52ee81d7d77c08a', - 'd7828d13b2b0bdc325a76236df93cc6b', - '0033568ef7b2633c9696766cfa'), - ( 'aa6cfa36cae86b40', - 'b916e0eacc1c00d7dcec68ec0b3bbb1a02de8a2d1aa346132e', - 'b1d23a2220ddc0ac900d9aa03c61fcf4a559a4417767089708', - 'a776796edb723506', - 'd7828d13b2b0bdc325a76236df93cc6b', - '00103fe41336713c9696766cfa'), - ( 'd0d0735c531e1becf049c244', - '12daac5630efa5396f770ce1a66b21f7b2101c', - '14d253c3967b70609b7cbb7c49916028324526', - '9a6f49975bcadeaf', - 'd7828d13b2b0bdc325a76236df93cc6b', - '00764c63b8058e3c9696766cfa'), - ( '77b60f011c03e1525899bcae', - 'e88b6a46c78d63e52eb8c546efb5de6f75e9cc0d', - '5545ff1a085ee2efbf52b2e04bee1e2336c73e3f', - '762c0c7744fe7e3c', - 'd7828d13b2b0bdc325a76236df93cc6b', - '00f8b678094e3b3c9696766cfa'), - ( 'cd9044d2b71fdb8120ea60c0', - '6435acbafb11a82e2f071d7ca4a5ebd93a803ba87f', - '009769ecabdf48625594c59251e6035722675e04c8', - '47099e5ae0704551', - 'd7828d13b2b0bdc325a76236df93cc6b', - '00d560912d3f703c9696766cfa'), - ( 'd85bc7e69f944fb8', - '8a19b950bcf71a018e5e6701c91787659809d67dbedd18', - 'bc218daa947427b6db386a99ac1aef23ade0b52939cb6a', - '637cf9bec2408897c6ba', - 'd7828d13b2b0bdc325a76236df93cc6b', - '0042fff8f1951c3c9696766cfa'), - ( '74a0ebc9069f5b37', - '1761433c37c5a35fc1f39f406302eb907c6163be38c98437', - '5810e6fd25874022e80361a478e3e9cf484ab04f447efff6', - 'f0a477cc2fc9bf548944', - 'd7828d13b2b0bdc325a76236df93cc6b', - '00920f40e56cdc3c9696766cfa'), - ( '44a3aa3aae6475ca', - 'a434a8e58500c6e41530538862d686ea9e81301b5ae4226bfa', - 'f2beed7bc5098e83feb5b31608f8e29c38819a89c8e776f154', - '4d4151a4ed3a8b87b9ce', - 'd7828d13b2b0bdc325a76236df93cc6b', - '0027ca0c7120bc3c9696766cfa'), - ( 'ec46bb63b02520c33c49fd70', - 'b96b49e21d621741632875db7f6c9243d2d7c2', - '31d750a09da3ed7fddd49a2032aabf17ec8ebf', - '7d22c8088c666be5c197', - 'd7828d13b2b0bdc325a76236df93cc6b', - '005b8ccbcd9af83c9696766cfa'), - ( '47a65ac78b3d594227e85e71', - 'e2fcfbb880442c731bf95167c8ffd7895e337076', - 'e882f1dbd38ce3eda7c23f04dd65071eb41342ac', - 'df7e00dccec7ae52987d', - 'd7828d13b2b0bdc325a76236df93cc6b', - '003ebe94044b9a3c9696766cfa'), - ( '6e37a6ef546d955d34ab6059', - 'abf21c0b02feb88f856df4a37381bce3cc128517d4', - 'f32905b88a641b04b9c9ffb58cc390900f3da12ab1', - '6dce9e82efa16da62059', - 'd7828d13b2b0bdc325a76236df93cc6b', - '008d493b30ae8b3c9696766cfa'), - ] - - test_vectors = [[unhexlify(x) for x in tv] for tv in test_vectors_hex] - - def runTest(self): - for assoc_data, pt, ct, mac, key, nonce in self.test_vectors: - # Encrypt - cipher = AES.new(key, AES.MODE_CCM, nonce, mac_len=len(mac)) - cipher.update(assoc_data) - ct2, mac2 = cipher.encrypt_and_digest(pt) - self.assertEqual(ct, ct2) - self.assertEqual(mac, mac2) - - # Decrypt - cipher = AES.new(key, AES.MODE_CCM, nonce, mac_len=len(mac)) - cipher.update(assoc_data) - pt2 = cipher.decrypt_and_verify(ct, mac) - self.assertEqual(pt, pt2) - - -class TestVectorsWycheproof(unittest.TestCase): - - def __init__(self, wycheproof_warnings, **extra_params): - unittest.TestCase.__init__(self) - self._wycheproof_warnings = wycheproof_warnings - self._extra_params = extra_params - self._id = "None" - - def setUp(self): - - def filter_tag(group): - return group['tagSize'] // 8 - - self.tv = load_test_vectors_wycheproof(("Cipher", "wycheproof"), - "aes_ccm_test.json", - "Wycheproof AES CCM", - group_tag={'tag_size': filter_tag}) - - def shortDescription(self): - return self._id - - def warn(self, tv): - if tv.warning and self._wycheproof_warnings: - import warnings - warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment)) - - def test_encrypt(self, tv): - self._id = "Wycheproof Encrypt CCM Test #" + str(tv.id) - - try: - cipher = AES.new(tv.key, AES.MODE_CCM, tv.iv, mac_len=tv.tag_size, - **self._extra_params) - except ValueError as e: - if len(tv.iv) not in range(7, 13 + 1, 2) and "Length of parameter 'nonce'" in str(e): - assert not tv.valid - return - if tv.tag_size not in range(4, 16 + 1, 2) and "Parameter 'mac_len'" in str(e): - assert not tv.valid - return - raise e - - cipher.update(tv.aad) - ct, tag = cipher.encrypt_and_digest(tv.msg) - if tv.valid: - self.assertEqual(ct, tv.ct) - self.assertEqual(tag, tv.tag) - self.warn(tv) - - def test_decrypt(self, tv): - self._id = "Wycheproof Decrypt CCM Test #" + str(tv.id) - - try: - cipher = AES.new(tv.key, AES.MODE_CCM, tv.iv, mac_len=tv.tag_size, - **self._extra_params) - except ValueError as e: - if len(tv.iv) not in range(7, 13 + 1, 2) and "Length of parameter 'nonce'" in str(e): - assert not tv.valid - return - if tv.tag_size not in range(4, 16 + 1, 2) and "Parameter 'mac_len'" in str(e): - assert not tv.valid - return - raise e - - cipher.update(tv.aad) - try: - pt = cipher.decrypt_and_verify(tv.ct, tv.tag) - except ValueError: - assert not tv.valid - else: - assert tv.valid - self.assertEqual(pt, tv.msg) - self.warn(tv) - - def test_corrupt_decrypt(self, tv): - self._id = "Wycheproof Corrupt Decrypt CCM Test #" + str(tv.id) - if len(tv.iv) not in range(7, 13 + 1, 2) or len(tv.ct) == 0: - return - cipher = AES.new(tv.key, AES.MODE_CCM, tv.iv, mac_len=tv.tag_size, - **self._extra_params) - cipher.update(tv.aad) - ct_corrupt = strxor(tv.ct, b"\x00" * (len(tv.ct) - 1) + b"\x01") - self.assertRaises(ValueError, cipher.decrypt_and_verify, ct_corrupt, tv.tag) - - def runTest(self): - - for tv in self.tv: - self.test_encrypt(tv) - self.test_decrypt(tv) - self.test_corrupt_decrypt(tv) - - -def get_tests(config={}): - wycheproof_warnings = config.get('wycheproof_warnings') - - tests = [] - tests += list_test_cases(CcmTests) - tests += list_test_cases(CcmFSMTests) - tests += [TestVectors()] - tests += [TestVectorsWycheproof(wycheproof_warnings)] - - return tests - - -if __name__ == '__main__': - def suite(): - unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CFB.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CFB.py deleted file mode 100644 index 673bf8e..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CFB.py +++ /dev/null @@ -1,411 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import unittest -from binascii import unhexlify - -from Cryptodome.SelfTest.loader import load_test_vectors -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.Util.py3compat import tobytes, is_string -from Cryptodome.Cipher import AES, DES3, DES -from Cryptodome.Hash import SHAKE128 - -from Cryptodome.SelfTest.Cipher.test_CBC import BlockChainingTests - - -def get_tag_random(tag, length): - return SHAKE128.new(data=tobytes(tag)).read(length) - - -class CfbTests(BlockChainingTests): - - aes_mode = AES.MODE_CFB - des3_mode = DES3.MODE_CFB - - # Redefine test_unaligned_data_128/64 - - def test_unaligned_data_128(self): - plaintexts = [ b"7777777" ] * 100 - - cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=8) - ciphertexts = [ cipher.encrypt(x) for x in plaintexts ] - cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=8) - self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts))) - - cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=128) - ciphertexts = [ cipher.encrypt(x) for x in plaintexts ] - cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=128) - self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts))) - - def test_unaligned_data_64(self): - plaintexts = [ b"7777777" ] * 100 - cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=8) - ciphertexts = [ cipher.encrypt(x) for x in plaintexts ] - cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=8) - self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts))) - - cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=64) - ciphertexts = [ cipher.encrypt(x) for x in plaintexts ] - cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=64) - self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts))) - - # Extra - - def test_segment_size_128(self): - for bits in range(8, 129, 8): - cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, - segment_size=bits) - - for bits in 0, 7, 9, 127, 129: - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CFB, - self.iv_128, - segment_size=bits) - - def test_segment_size_64(self): - for bits in range(8, 65, 8): - cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, - segment_size=bits) - - for bits in 0, 7, 9, 63, 65: - self.assertRaises(ValueError, DES3.new, self.key_192, AES.MODE_CFB, - self.iv_64, - segment_size=bits) - - -class NistCfbVectors(unittest.TestCase): - - def _do_kat_aes_test(self, file_name, segment_size): - - test_vectors = load_test_vectors(("Cipher", "AES"), - file_name, - "AES CFB%d KAT" % segment_size, - { "count" : lambda x: int(x) } ) - if test_vectors is None: - return - - direction = None - for tv in test_vectors: - - # The test vector file contains some directive lines - if is_string(tv): - direction = tv - continue - - self.description = tv.desc - cipher = AES.new(tv.key, AES.MODE_CFB, tv.iv, - segment_size=segment_size) - if direction == "[ENCRYPT]": - self.assertEqual(cipher.encrypt(tv.plaintext), tv.ciphertext) - elif direction == "[DECRYPT]": - self.assertEqual(cipher.decrypt(tv.ciphertext), tv.plaintext) - else: - assert False - - # See Section 6.4.5 in AESAVS - def _do_mct_aes_test(self, file_name, segment_size): - - test_vectors = load_test_vectors(("Cipher", "AES"), - file_name, - "AES CFB%d Montecarlo" % segment_size, - { "count" : lambda x: int(x) } ) - if test_vectors is None: - return - - assert(segment_size in (8, 128)) - - direction = None - for tv in test_vectors: - - # The test vector file contains some directive lines - if is_string(tv): - direction = tv - continue - - self.description = tv.desc - cipher = AES.new(tv.key, AES.MODE_CFB, tv.iv, - segment_size=segment_size) - - def get_input(input_text, output_seq, j): - # CFB128 - if segment_size == 128: - if j >= 2: - return output_seq[-2] - return [input_text, tv.iv][j] - # CFB8 - if j == 0: - return input_text - elif j <= 16: - return tv.iv[j - 1:j] - return output_seq[j - 17] - - if direction == '[ENCRYPT]': - cts = [] - for j in range(1000): - plaintext = get_input(tv.plaintext, cts, j) - cts.append(cipher.encrypt(plaintext)) - self.assertEqual(cts[-1], tv.ciphertext) - elif direction == '[DECRYPT]': - pts = [] - for j in range(1000): - ciphertext = get_input(tv.ciphertext, pts, j) - pts.append(cipher.decrypt(ciphertext)) - self.assertEqual(pts[-1], tv.plaintext) - else: - assert False - - def _do_tdes_test(self, file_name, segment_size): - - test_vectors = load_test_vectors(("Cipher", "TDES"), - file_name, - "TDES CFB%d KAT" % segment_size, - { "count" : lambda x: int(x) } ) - if test_vectors is None: - return - - direction = None - for tv in test_vectors: - - # The test vector file contains some directive lines - if is_string(tv): - direction = tv - continue - - self.description = tv.desc - if hasattr(tv, "keys"): - cipher = DES.new(tv.keys, DES.MODE_CFB, tv.iv, - segment_size=segment_size) - else: - if tv.key1 != tv.key3: - key = tv.key1 + tv.key2 + tv.key3 # Option 3 - else: - key = tv.key1 + tv.key2 # Option 2 - cipher = DES3.new(key, DES3.MODE_CFB, tv.iv, - segment_size=segment_size) - if direction == "[ENCRYPT]": - self.assertEqual(cipher.encrypt(tv.plaintext), tv.ciphertext) - elif direction == "[DECRYPT]": - self.assertEqual(cipher.decrypt(tv.ciphertext), tv.plaintext) - else: - assert False - - -# Create one test method per file -nist_aes_kat_mmt_files = ( - # KAT - "CFB?GFSbox128.rsp", - "CFB?GFSbox192.rsp", - "CFB?GFSbox256.rsp", - "CFB?KeySbox128.rsp", - "CFB?KeySbox192.rsp", - "CFB?KeySbox256.rsp", - "CFB?VarKey128.rsp", - "CFB?VarKey192.rsp", - "CFB?VarKey256.rsp", - "CFB?VarTxt128.rsp", - "CFB?VarTxt192.rsp", - "CFB?VarTxt256.rsp", - # MMT - "CFB?MMT128.rsp", - "CFB?MMT192.rsp", - "CFB?MMT256.rsp", - ) -nist_aes_mct_files = ( - "CFB?MCT128.rsp", - "CFB?MCT192.rsp", - "CFB?MCT256.rsp", - ) - -for file_gen_name in nist_aes_kat_mmt_files: - for bits in "8", "128": - file_name = file_gen_name.replace("?", bits) - def new_func(self, file_name=file_name, bits=bits): - self._do_kat_aes_test(file_name, int(bits)) - setattr(NistCfbVectors, "test_AES_" + file_name, new_func) - -for file_gen_name in nist_aes_mct_files: - for bits in "8", "128": - file_name = file_gen_name.replace("?", bits) - def new_func(self, file_name=file_name, bits=bits): - self._do_mct_aes_test(file_name, int(bits)) - setattr(NistCfbVectors, "test_AES_" + file_name, new_func) -del file_name, new_func - -nist_tdes_files = ( - "TCFB?MMT2.rsp", # 2TDES - "TCFB?MMT3.rsp", # 3TDES - "TCFB?invperm.rsp", # Single DES - "TCFB?permop.rsp", - "TCFB?subtab.rsp", - "TCFB?varkey.rsp", - "TCFB?vartext.rsp", - ) - -for file_gen_name in nist_tdes_files: - for bits in "8", "64": - file_name = file_gen_name.replace("?", bits) - def new_func(self, file_name=file_name, bits=bits): - self._do_tdes_test(file_name, int(bits)) - setattr(NistCfbVectors, "test_TDES_" + file_name, new_func) - -# END OF NIST CBC TEST VECTORS - - -class SP800TestVectors(unittest.TestCase): - """Class exercising the CFB test vectors found in Section F.3 - of NIST SP 800-3A""" - - def test_aes_128_cfb8(self): - plaintext = '6bc1bee22e409f96e93d7e117393172aae2d' - ciphertext = '3b79424c9c0dd436bace9e0ed4586a4f32b9' - key = '2b7e151628aed2a6abf7158809cf4f3c' - iv = '000102030405060708090a0b0c0d0e0f' - - key = unhexlify(key) - iv = unhexlify(iv) - plaintext = unhexlify(plaintext) - ciphertext = unhexlify(ciphertext) - - cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=8) - self.assertEqual(cipher.encrypt(plaintext), ciphertext) - cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=8) - self.assertEqual(cipher.decrypt(ciphertext), plaintext) - - def test_aes_192_cfb8(self): - plaintext = '6bc1bee22e409f96e93d7e117393172aae2d' - ciphertext = 'cda2521ef0a905ca44cd057cbf0d47a0678a' - key = '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b' - iv = '000102030405060708090a0b0c0d0e0f' - - key = unhexlify(key) - iv = unhexlify(iv) - plaintext = unhexlify(plaintext) - ciphertext = unhexlify(ciphertext) - - cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=8) - self.assertEqual(cipher.encrypt(plaintext), ciphertext) - cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=8) - self.assertEqual(cipher.decrypt(ciphertext), plaintext) - - def test_aes_256_cfb8(self): - plaintext = '6bc1bee22e409f96e93d7e117393172aae2d' - ciphertext = 'dc1f1a8520a64db55fcc8ac554844e889700' - key = '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4' - iv = '000102030405060708090a0b0c0d0e0f' - - key = unhexlify(key) - iv = unhexlify(iv) - plaintext = unhexlify(plaintext) - ciphertext = unhexlify(ciphertext) - - cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=8) - self.assertEqual(cipher.encrypt(plaintext), ciphertext) - cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=8) - self.assertEqual(cipher.decrypt(ciphertext), plaintext) - - def test_aes_128_cfb128(self): - plaintext = '6bc1bee22e409f96e93d7e117393172a' +\ - 'ae2d8a571e03ac9c9eb76fac45af8e51' +\ - '30c81c46a35ce411e5fbc1191a0a52ef' +\ - 'f69f2445df4f9b17ad2b417be66c3710' - ciphertext = '3b3fd92eb72dad20333449f8e83cfb4a' +\ - 'c8a64537a0b3a93fcde3cdad9f1ce58b' +\ - '26751f67a3cbb140b1808cf187a4f4df' +\ - 'c04b05357c5d1c0eeac4c66f9ff7f2e6' - key = '2b7e151628aed2a6abf7158809cf4f3c' - iv = '000102030405060708090a0b0c0d0e0f' - - key = unhexlify(key) - iv = unhexlify(iv) - plaintext = unhexlify(plaintext) - ciphertext = unhexlify(ciphertext) - - cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128) - self.assertEqual(cipher.encrypt(plaintext), ciphertext) - cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128) - self.assertEqual(cipher.decrypt(ciphertext), plaintext) - - def test_aes_192_cfb128(self): - plaintext = '6bc1bee22e409f96e93d7e117393172a' +\ - 'ae2d8a571e03ac9c9eb76fac45af8e51' +\ - '30c81c46a35ce411e5fbc1191a0a52ef' +\ - 'f69f2445df4f9b17ad2b417be66c3710' - ciphertext = 'cdc80d6fddf18cab34c25909c99a4174' +\ - '67ce7f7f81173621961a2b70171d3d7a' +\ - '2e1e8a1dd59b88b1c8e60fed1efac4c9' +\ - 'c05f9f9ca9834fa042ae8fba584b09ff' - key = '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b' - iv = '000102030405060708090a0b0c0d0e0f' - - key = unhexlify(key) - iv = unhexlify(iv) - plaintext = unhexlify(plaintext) - ciphertext = unhexlify(ciphertext) - - cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128) - self.assertEqual(cipher.encrypt(plaintext), ciphertext) - cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128) - self.assertEqual(cipher.decrypt(ciphertext), plaintext) - - def test_aes_256_cfb128(self): - plaintext = '6bc1bee22e409f96e93d7e117393172a' +\ - 'ae2d8a571e03ac9c9eb76fac45af8e51' +\ - '30c81c46a35ce411e5fbc1191a0a52ef' +\ - 'f69f2445df4f9b17ad2b417be66c3710' - - ciphertext = 'dc7e84bfda79164b7ecd8486985d3860' +\ - '39ffed143b28b1c832113c6331e5407b' +\ - 'df10132415e54b92a13ed0a8267ae2f9' +\ - '75a385741ab9cef82031623d55b1e471' - key = '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4' - iv = '000102030405060708090a0b0c0d0e0f' - - key = unhexlify(key) - iv = unhexlify(iv) - plaintext = unhexlify(plaintext) - ciphertext = unhexlify(ciphertext) - - cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128) - self.assertEqual(cipher.encrypt(plaintext), ciphertext) - cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128) - self.assertEqual(cipher.decrypt(ciphertext), plaintext) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(CfbTests) - if config.get('slow_tests'): - tests += list_test_cases(NistCfbVectors) - tests += list_test_cases(SP800TestVectors) - return tests - - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CTR.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CTR.py deleted file mode 100644 index ef5be5d..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_CTR.py +++ /dev/null @@ -1,472 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2015, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import unittest -from binascii import hexlify, unhexlify - -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.Util.py3compat import tobytes, bchr -from Cryptodome.Cipher import AES, DES3 -from Cryptodome.Hash import SHAKE128, SHA256 -from Cryptodome.Util import Counter - -def get_tag_random(tag, length): - return SHAKE128.new(data=tobytes(tag)).read(length) - -class CtrTests(unittest.TestCase): - - key_128 = get_tag_random("key_128", 16) - key_192 = get_tag_random("key_192", 24) - nonce_32 = get_tag_random("nonce_32", 4) - nonce_64 = get_tag_random("nonce_64", 8) - ctr_64 = Counter.new(32, prefix=nonce_32) - ctr_128 = Counter.new(64, prefix=nonce_64) - - def test_loopback_128(self): - cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128) - pt = get_tag_random("plaintext", 16 * 100) - ct = cipher.encrypt(pt) - - cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128) - pt2 = cipher.decrypt(ct) - self.assertEqual(pt, pt2) - - def test_loopback_64(self): - cipher = DES3.new(self.key_192, DES3.MODE_CTR, counter=self.ctr_64) - pt = get_tag_random("plaintext", 8 * 100) - ct = cipher.encrypt(pt) - - cipher = DES3.new(self.key_192, DES3.MODE_CTR, counter=self.ctr_64) - pt2 = cipher.decrypt(ct) - self.assertEqual(pt, pt2) - - def test_invalid_counter_parameter(self): - # Counter object is required for ciphers with short block size - self.assertRaises(TypeError, DES3.new, self.key_192, AES.MODE_CTR) - # Positional arguments are not allowed (Counter must be passed as - # keyword) - self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CTR, self.ctr_128) - - def test_nonce_attribute(self): - # Nonce attribute is the prefix passed to Counter (DES3) - cipher = DES3.new(self.key_192, DES3.MODE_CTR, counter=self.ctr_64) - self.assertEqual(cipher.nonce, self.nonce_32) - - # Nonce attribute is the prefix passed to Counter (AES) - cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128) - self.assertEqual(cipher.nonce, self.nonce_64) - - # Nonce attribute is not defined if suffix is used in Counter - counter = Counter.new(64, prefix=self.nonce_32, suffix=self.nonce_32) - cipher = AES.new(self.key_128, AES.MODE_CTR, counter=counter) - self.assertFalse(hasattr(cipher, "nonce")) - - def test_nonce_parameter(self): - # Nonce parameter becomes nonce attribute - cipher1 = AES.new(self.key_128, AES.MODE_CTR, nonce=self.nonce_64) - self.assertEqual(cipher1.nonce, self.nonce_64) - - counter = Counter.new(64, prefix=self.nonce_64, initial_value=0) - cipher2 = AES.new(self.key_128, AES.MODE_CTR, counter=counter) - self.assertEqual(cipher1.nonce, cipher2.nonce) - - pt = get_tag_random("plaintext", 65536) - self.assertEqual(cipher1.encrypt(pt), cipher2.encrypt(pt)) - - # Nonce is implicitly created (for AES) when no parameters are passed - nonce1 = AES.new(self.key_128, AES.MODE_CTR).nonce - nonce2 = AES.new(self.key_128, AES.MODE_CTR).nonce - self.assertNotEqual(nonce1, nonce2) - self.assertEqual(len(nonce1), 8) - - # Nonce can be zero-length - cipher = AES.new(self.key_128, AES.MODE_CTR, nonce=b"") - self.assertEqual(b"", cipher.nonce) - cipher.encrypt(b'0'*300) - - # Nonce and Counter are mutually exclusive - self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CTR, - counter=self.ctr_128, nonce=self.nonce_64) - - def test_initial_value_parameter(self): - # Test with nonce parameter - cipher1 = AES.new(self.key_128, AES.MODE_CTR, - nonce=self.nonce_64, initial_value=0xFFFF) - counter = Counter.new(64, prefix=self.nonce_64, initial_value=0xFFFF) - cipher2 = AES.new(self.key_128, AES.MODE_CTR, counter=counter) - pt = get_tag_random("plaintext", 65536) - self.assertEqual(cipher1.encrypt(pt), cipher2.encrypt(pt)) - - # Test without nonce parameter - cipher1 = AES.new(self.key_128, AES.MODE_CTR, - initial_value=0xFFFF) - counter = Counter.new(64, prefix=cipher1.nonce, initial_value=0xFFFF) - cipher2 = AES.new(self.key_128, AES.MODE_CTR, counter=counter) - pt = get_tag_random("plaintext", 65536) - self.assertEqual(cipher1.encrypt(pt), cipher2.encrypt(pt)) - - # Initial_value and Counter are mutually exclusive - self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CTR, - counter=self.ctr_128, initial_value=0) - - def test_initial_value_bytes_parameter(self): - # Same result as when passing an integer - cipher1 = AES.new(self.key_128, AES.MODE_CTR, - nonce=self.nonce_64, - initial_value=b"\x00"*6+b"\xFF\xFF") - cipher2 = AES.new(self.key_128, AES.MODE_CTR, - nonce=self.nonce_64, initial_value=0xFFFF) - pt = get_tag_random("plaintext", 65536) - self.assertEqual(cipher1.encrypt(pt), cipher2.encrypt(pt)) - - # Fail if the iv is too large - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CTR, - initial_value=b"5"*17) - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CTR, - nonce=self.nonce_64, initial_value=b"5"*9) - - # Fail if the iv is too short - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CTR, - initial_value=b"5"*15) - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CTR, - nonce=self.nonce_64, initial_value=b"5"*7) - - def test_iv_with_matching_length(self): - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CTR, - counter=Counter.new(120)) - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CTR, - counter=Counter.new(136)) - - def test_block_size_128(self): - cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128) - self.assertEqual(cipher.block_size, AES.block_size) - - def test_block_size_64(self): - cipher = DES3.new(self.key_192, DES3.MODE_CTR, counter=self.ctr_64) - self.assertEqual(cipher.block_size, DES3.block_size) - - def test_unaligned_data_128(self): - plaintexts = [ b"7777777" ] * 100 - - cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128) - ciphertexts = [ cipher.encrypt(x) for x in plaintexts ] - cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128) - self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts))) - - cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128) - ciphertexts = [ cipher.encrypt(x) for x in plaintexts ] - cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128) - self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts))) - - def test_unaligned_data_64(self): - plaintexts = [ b"7777777" ] * 100 - cipher = DES3.new(self.key_192, AES.MODE_CTR, counter=self.ctr_64) - ciphertexts = [ cipher.encrypt(x) for x in plaintexts ] - cipher = DES3.new(self.key_192, AES.MODE_CTR, counter=self.ctr_64) - self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts))) - - cipher = DES3.new(self.key_192, AES.MODE_CTR, counter=self.ctr_64) - ciphertexts = [ cipher.encrypt(x) for x in plaintexts ] - cipher = DES3.new(self.key_192, AES.MODE_CTR, counter=self.ctr_64) - self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts))) - - def test_unknown_parameters(self): - self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CTR, - 7, counter=self.ctr_128) - self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CTR, - counter=self.ctr_128, unknown=7) - # But some are only known by the base cipher (e.g. use_aesni consumed by the AES module) - AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128, use_aesni=False) - - def test_null_encryption_decryption(self): - for func in "encrypt", "decrypt": - cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128) - result = getattr(cipher, func)(b"") - self.assertEqual(result, b"") - - def test_either_encrypt_or_decrypt(self): - cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128) - cipher.encrypt(b"") - self.assertRaises(TypeError, cipher.decrypt, b"") - - cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128) - cipher.decrypt(b"") - self.assertRaises(TypeError, cipher.encrypt, b"") - - def test_wrap_around(self): - # Counter is only 8 bits, so we can only encrypt/decrypt 256 blocks (=4096 bytes) - counter = Counter.new(8, prefix=bchr(9) * 15) - max_bytes = 4096 - - cipher = AES.new(self.key_128, AES.MODE_CTR, counter=counter) - cipher.encrypt(b'9' * max_bytes) - self.assertRaises(OverflowError, cipher.encrypt, b'9') - - cipher = AES.new(self.key_128, AES.MODE_CTR, counter=counter) - self.assertRaises(OverflowError, cipher.encrypt, b'9' * (max_bytes + 1)) - - cipher = AES.new(self.key_128, AES.MODE_CTR, counter=counter) - cipher.decrypt(b'9' * max_bytes) - self.assertRaises(OverflowError, cipher.decrypt, b'9') - - cipher = AES.new(self.key_128, AES.MODE_CTR, counter=counter) - self.assertRaises(OverflowError, cipher.decrypt, b'9' * (max_bytes + 1)) - - def test_bytearray(self): - data = b"1" * 16 - iv = b"\x00" * 6 + b"\xFF\xFF" - - # Encrypt - cipher1 = AES.new(self.key_128, AES.MODE_CTR, - nonce=self.nonce_64, - initial_value=iv) - ref1 = cipher1.encrypt(data) - - cipher2 = AES.new(self.key_128, AES.MODE_CTR, - nonce=bytearray(self.nonce_64), - initial_value=bytearray(iv)) - ref2 = cipher2.encrypt(bytearray(data)) - - self.assertEqual(ref1, ref2) - self.assertEqual(cipher1.nonce, cipher2.nonce) - - # Decrypt - cipher3 = AES.new(self.key_128, AES.MODE_CTR, - nonce=self.nonce_64, - initial_value=iv) - ref3 = cipher3.decrypt(data) - - cipher4 = AES.new(self.key_128, AES.MODE_CTR, - nonce=bytearray(self.nonce_64), - initial_value=bytearray(iv)) - ref4 = cipher4.decrypt(bytearray(data)) - - self.assertEqual(ref3, ref4) - - def test_very_long_data(self): - cipher = AES.new(b'A' * 32, AES.MODE_CTR, nonce=b'') - ct = cipher.encrypt(b'B' * 1000000) - digest = SHA256.new(ct).hexdigest() - self.assertEqual(digest, "96204fc470476561a3a8f3b6fe6d24be85c87510b638142d1d0fb90989f8a6a6") - - def test_output_param(self): - - pt = b'5' * 128 - cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64) - ct = cipher.encrypt(pt) - - output = bytearray(128) - cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64) - res = cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - self.assertEqual(res, None) - - cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64) - res = cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - self.assertEqual(res, None) - - def test_output_param_memoryview(self): - - pt = b'5' * 128 - cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64) - ct = cipher.encrypt(pt) - - output = memoryview(bytearray(128)) - cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64) - cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - - cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64) - cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - - def test_output_param_neg(self): - LEN_PT = 128 - - pt = b'5' * LEN_PT - cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64) - ct = cipher.encrypt(pt) - - cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64) - self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0' * LEN_PT) - - cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64) - self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0' * LEN_PT) - - shorter_output = bytearray(LEN_PT - 1) - cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64) - self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output) - cipher = AES.new(b'4'*16, AES.MODE_CTR, nonce=self.nonce_64) - self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output) - - -class SP800TestVectors(unittest.TestCase): - """Class exercising the CTR test vectors found in Section F.5 - of NIST SP 800-38A""" - - def test_aes_128(self): - plaintext = '6bc1bee22e409f96e93d7e117393172a' +\ - 'ae2d8a571e03ac9c9eb76fac45af8e51' +\ - '30c81c46a35ce411e5fbc1191a0a52ef' +\ - 'f69f2445df4f9b17ad2b417be66c3710' - ciphertext = '874d6191b620e3261bef6864990db6ce' +\ - '9806f66b7970fdff8617187bb9fffdff' +\ - '5ae4df3edbd5d35e5b4f09020db03eab' +\ - '1e031dda2fbe03d1792170a0f3009cee' - key = '2b7e151628aed2a6abf7158809cf4f3c' - counter = Counter.new(nbits=16, - prefix=unhexlify('f0f1f2f3f4f5f6f7f8f9fafbfcfd'), - initial_value=0xfeff) - - key = unhexlify(key) - plaintext = unhexlify(plaintext) - ciphertext = unhexlify(ciphertext) - - cipher = AES.new(key, AES.MODE_CTR, counter=counter) - self.assertEqual(cipher.encrypt(plaintext), ciphertext) - cipher = AES.new(key, AES.MODE_CTR, counter=counter) - self.assertEqual(cipher.decrypt(ciphertext), plaintext) - - def test_aes_192(self): - plaintext = '6bc1bee22e409f96e93d7e117393172a' +\ - 'ae2d8a571e03ac9c9eb76fac45af8e51' +\ - '30c81c46a35ce411e5fbc1191a0a52ef' +\ - 'f69f2445df4f9b17ad2b417be66c3710' - ciphertext = '1abc932417521ca24f2b0459fe7e6e0b' +\ - '090339ec0aa6faefd5ccc2c6f4ce8e94' +\ - '1e36b26bd1ebc670d1bd1d665620abf7' +\ - '4f78a7f6d29809585a97daec58c6b050' - key = '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b' - counter = Counter.new(nbits=16, - prefix=unhexlify('f0f1f2f3f4f5f6f7f8f9fafbfcfd'), - initial_value=0xfeff) - - key = unhexlify(key) - plaintext = unhexlify(plaintext) - ciphertext = unhexlify(ciphertext) - - cipher = AES.new(key, AES.MODE_CTR, counter=counter) - self.assertEqual(cipher.encrypt(plaintext), ciphertext) - cipher = AES.new(key, AES.MODE_CTR, counter=counter) - self.assertEqual(cipher.decrypt(ciphertext), plaintext) - - def test_aes_256(self): - plaintext = '6bc1bee22e409f96e93d7e117393172a' +\ - 'ae2d8a571e03ac9c9eb76fac45af8e51' +\ - '30c81c46a35ce411e5fbc1191a0a52ef' +\ - 'f69f2445df4f9b17ad2b417be66c3710' - ciphertext = '601ec313775789a5b7a7f504bbf3d228' +\ - 'f443e3ca4d62b59aca84e990cacaf5c5' +\ - '2b0930daa23de94ce87017ba2d84988d' +\ - 'dfc9c58db67aada613c2dd08457941a6' - key = '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4' - counter = Counter.new(nbits=16, - prefix=unhexlify('f0f1f2f3f4f5f6f7f8f9fafbfcfd'), - initial_value=0xfeff) - key = unhexlify(key) - plaintext = unhexlify(plaintext) - ciphertext = unhexlify(ciphertext) - - cipher = AES.new(key, AES.MODE_CTR, counter=counter) - self.assertEqual(cipher.encrypt(plaintext), ciphertext) - cipher = AES.new(key, AES.MODE_CTR, counter=counter) - self.assertEqual(cipher.decrypt(ciphertext), plaintext) - - -class RFC3686TestVectors(unittest.TestCase): - - # Each item is a test vector with: - # - plaintext - # - ciphertext - # - key (AES 128, 192 or 256 bits) - # - counter prefix (4 byte nonce + 8 byte nonce) - data = ( - ('53696e676c6520626c6f636b206d7367', - 'e4095d4fb7a7b3792d6175a3261311b8', - 'ae6852f8121067cc4bf7a5765577f39e', - '000000300000000000000000'), - ('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', - '5104a106168a72d9790d41ee8edad388eb2e1efc46da57c8fce630df9141be28', - '7e24067817fae0d743d6ce1f32539163', - '006cb6dbc0543b59da48d90b'), - ('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223', - 'c1cf48a89f2ffdd9cf4652e9efdb72d74540a42bde6d7836d59a5ceaaef3105325b2072f', - '7691be035e5020a8ac6e618529f9a0dc', - '00e0017b27777f3f4a1786f0'), - ('53696e676c6520626c6f636b206d7367', - '4b55384fe259c9c84e7935a003cbe928', - '16af5b145fc9f579c175f93e3bfb0eed863d06ccfdb78515', - '0000004836733c147d6d93cb'), - ('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', - '453243fc609b23327edfaafa7131cd9f8490701c5ad4a79cfc1fe0ff42f4fb00', - '7c5cb2401b3dc33c19e7340819e0f69c678c3db8e6f6a91a', - '0096b03b020c6eadc2cb500d'), - ('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223', - '96893fc55e5c722f540b7dd1ddf7e758d288bc95c69165884536c811662f2188abee0935', - '02bf391ee8ecb159b959617b0965279bf59b60a786d3e0fe', - '0007bdfd5cbd60278dcc0912'), - ('53696e676c6520626c6f636b206d7367', - '145ad01dbf824ec7560863dc71e3e0c0', - '776beff2851db06f4c8a0542c8696f6c6a81af1eec96b4d37fc1d689e6c1c104', - '00000060db5672c97aa8f0b2'), - ('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', - 'f05e231b3894612c49ee000b804eb2a9b8306b508f839d6a5530831d9344af1c', - 'f6d66d6bd52d59bb0796365879eff886c66dd51a5b6a99744b50590c87a23884', - '00faac24c1585ef15a43d875'), - ('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223', - 'eb6c52821d0bbbf7ce7594462aca4faab407df866569fd07f48cc0b583d6071f1ec0e6b8', - 'ff7a617ce69148e4f1726e2f43581de2aa62d9f805532edff1eed687fb54153d', - '001cc5b751a51d70a1c11148') - ) - - bindata = [] - for tv in data: - bindata.append([unhexlify(x) for x in tv]) - - def runTest(self): - for pt, ct, key, prefix in self.bindata: - counter = Counter.new(32, prefix=prefix) - cipher = AES.new(key, AES.MODE_CTR, counter=counter) - result = cipher.encrypt(pt) - self.assertEqual(hexlify(ct), hexlify(result)) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(CtrTests) - tests += list_test_cases(SP800TestVectors) - tests += [ RFC3686TestVectors() ] - return tests - - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_ChaCha20.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_ChaCha20.py deleted file mode 100644 index 92c6f3c..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_ChaCha20.py +++ /dev/null @@ -1,529 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import os -import re -import unittest -from binascii import hexlify, unhexlify - -from Cryptodome.Util.py3compat import b, tobytes, bchr -from Cryptodome.Util.strxor import strxor_c -from Cryptodome.SelfTest.st_common import list_test_cases - -from Cryptodome.Cipher import ChaCha20 - - -class ChaCha20Test(unittest.TestCase): - - def test_new_positive(self): - cipher = ChaCha20.new(key=b("0")*32, nonce=b"0"*8) - self.assertEqual(cipher.nonce, b"0" * 8) - cipher = ChaCha20.new(key=b("0")*32, nonce=b"0"*12) - self.assertEqual(cipher.nonce, b"0" * 12) - - def test_new_negative(self): - new = ChaCha20.new - self.assertRaises(TypeError, new) - self.assertRaises(TypeError, new, nonce=b("0")) - self.assertRaises(ValueError, new, nonce=b("0")*8, key=b("0")) - self.assertRaises(ValueError, new, nonce=b("0"), key=b("0")*32) - - def test_default_nonce(self): - cipher1 = ChaCha20.new(key=bchr(1) * 32) - cipher2 = ChaCha20.new(key=bchr(1) * 32) - self.assertEqual(len(cipher1.nonce), 8) - self.assertNotEqual(cipher1.nonce, cipher2.nonce) - - def test_nonce(self): - key = b'A' * 32 - - nonce1 = b'P' * 8 - cipher1 = ChaCha20.new(key=key, nonce=nonce1) - self.assertEqual(nonce1, cipher1.nonce) - - nonce2 = b'Q' * 12 - cipher2 = ChaCha20.new(key=key, nonce=nonce2) - self.assertEqual(nonce2, cipher2.nonce) - - def test_eiter_encrypt_or_decrypt(self): - """Verify that a cipher cannot be used for both decrypting and encrypting""" - - c1 = ChaCha20.new(key=b("5") * 32, nonce=b("6") * 8) - c1.encrypt(b("8")) - self.assertRaises(TypeError, c1.decrypt, b("9")) - - c2 = ChaCha20.new(key=b("5") * 32, nonce=b("6") * 8) - c2.decrypt(b("8")) - self.assertRaises(TypeError, c2.encrypt, b("9")) - - def test_round_trip(self): - pt = b("A") * 1024 - c1 = ChaCha20.new(key=b("5") * 32, nonce=b("6") * 8) - c2 = ChaCha20.new(key=b("5") * 32, nonce=b("6") * 8) - ct = c1.encrypt(pt) - self.assertEqual(c2.decrypt(ct), pt) - - self.assertEqual(c1.encrypt(b("")), b("")) - self.assertEqual(c2.decrypt(b("")), b("")) - - def test_streaming(self): - """Verify that an arbitrary number of bytes can be encrypted/decrypted""" - from Cryptodome.Hash import SHA1 - - segments = (1, 3, 5, 7, 11, 17, 23) - total = sum(segments) - - pt = b("") - while len(pt) < total: - pt += SHA1.new(pt).digest() - - cipher1 = ChaCha20.new(key=b("7") * 32, nonce=b("t") * 8) - ct = cipher1.encrypt(pt) - - cipher2 = ChaCha20.new(key=b("7") * 32, nonce=b("t") * 8) - cipher3 = ChaCha20.new(key=b("7") * 32, nonce=b("t") * 8) - idx = 0 - for segment in segments: - self.assertEqual(cipher2.decrypt(ct[idx:idx+segment]), pt[idx:idx+segment]) - self.assertEqual(cipher3.encrypt(pt[idx:idx+segment]), ct[idx:idx+segment]) - idx += segment - - def test_seek(self): - cipher1 = ChaCha20.new(key=b("9") * 32, nonce=b("e") * 8) - - offset = 64 * 900 + 7 - pt = b("1") * 64 - - cipher1.encrypt(b("0") * offset) - ct1 = cipher1.encrypt(pt) - - cipher2 = ChaCha20.new(key=b("9") * 32, nonce=b("e") * 8) - cipher2.seek(offset) - ct2 = cipher2.encrypt(pt) - - self.assertEqual(ct1, ct2) - - def test_seek_tv(self): - # Test Vector #4, A.1 from - # http://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04 - key = bchr(0) + bchr(255) + bchr(0) * 30 - nonce = bchr(0) * 8 - cipher = ChaCha20.new(key=key, nonce=nonce) - cipher.seek(64 * 2) - expected_key_stream = unhexlify(b( - "72d54dfbf12ec44b362692df94137f32" - "8fea8da73990265ec1bbbea1ae9af0ca" - "13b25aa26cb4a648cb9b9d1be65b2c09" - "24a66c54d545ec1b7374f4872e99f096" - )) - ct = cipher.encrypt(bchr(0) * len(expected_key_stream)) - self.assertEqual(expected_key_stream, ct) - - def test_rfc7539(self): - # from https://tools.ietf.org/html/rfc7539 Annex A.1 - # Each item is: key, nonce, block #, plaintext, ciphertext - tvs = [ - # Test Vector #1 - ( - "00"*32, - "00"*12, - 0, - "00"*16*4, - "76b8e0ada0f13d90405d6ae55386bd28" - "bdd219b8a08ded1aa836efcc8b770dc7" - "da41597c5157488d7724e03fb8d84a37" - "6a43b8f41518a11cc387b669b2ee6586" - ), - # Test Vector #2 - ( - "00"*31 + "01", - "00"*11 + "02", - 1, - "416e79207375626d697373696f6e2074" - "6f20746865204945544620696e74656e" - "6465642062792074686520436f6e7472" - "696275746f7220666f72207075626c69" - "636174696f6e20617320616c6c206f72" - "2070617274206f6620616e2049455446" - "20496e7465726e65742d447261667420" - "6f722052464320616e6420616e792073" - "746174656d656e74206d616465207769" - "7468696e2074686520636f6e74657874" - "206f6620616e20494554462061637469" - "7669747920697320636f6e7369646572" - "656420616e20224945544620436f6e74" - "7269627574696f6e222e205375636820" - "73746174656d656e747320696e636c75" - "6465206f72616c2073746174656d656e" - "747320696e2049455446207365737369" - "6f6e732c2061732077656c6c20617320" - "7772697474656e20616e6420656c6563" - "74726f6e696320636f6d6d756e696361" - "74696f6e73206d61646520617420616e" - "792074696d65206f7220706c6163652c" - "20776869636820617265206164647265" - "7373656420746f", - "a3fbf07df3fa2fde4f376ca23e827370" - "41605d9f4f4f57bd8cff2c1d4b7955ec" - "2a97948bd3722915c8f3d337f7d37005" - "0e9e96d647b7c39f56e031ca5eb6250d" - "4042e02785ececfa4b4bb5e8ead0440e" - "20b6e8db09d881a7c6132f420e527950" - "42bdfa7773d8a9051447b3291ce1411c" - "680465552aa6c405b7764d5e87bea85a" - "d00f8449ed8f72d0d662ab052691ca66" - "424bc86d2df80ea41f43abf937d3259d" - "c4b2d0dfb48a6c9139ddd7f76966e928" - "e635553ba76c5c879d7b35d49eb2e62b" - "0871cdac638939e25e8a1e0ef9d5280f" - "a8ca328b351c3c765989cbcf3daa8b6c" - "cc3aaf9f3979c92b3720fc88dc95ed84" - "a1be059c6499b9fda236e7e818b04b0b" - "c39c1e876b193bfe5569753f88128cc0" - "8aaa9b63d1a16f80ef2554d7189c411f" - "5869ca52c5b83fa36ff216b9c1d30062" - "bebcfd2dc5bce0911934fda79a86f6e6" - "98ced759c3ff9b6477338f3da4f9cd85" - "14ea9982ccafb341b2384dd902f3d1ab" - "7ac61dd29c6f21ba5b862f3730e37cfd" - "c4fd806c22f221" - ), - # Test Vector #3 - ( - "1c9240a5eb55d38af333888604f6b5f0" - "473917c1402b80099dca5cbc207075c0", - "00"*11 + "02", - 42, - "2754776173206272696c6c69672c2061" - "6e642074686520736c6974687920746f" - "7665730a446964206779726520616e64" - "2067696d626c6520696e207468652077" - "6162653a0a416c6c206d696d73792077" - "6572652074686520626f726f676f7665" - "732c0a416e6420746865206d6f6d6520" - "7261746873206f757467726162652e", - "62e6347f95ed87a45ffae7426f27a1df" - "5fb69110044c0d73118effa95b01e5cf" - "166d3df2d721caf9b21e5fb14c616871" - "fd84c54f9d65b283196c7fe4f60553eb" - "f39c6402c42234e32a356b3e764312a6" - "1a5532055716ead6962568f87d3f3f77" - "04c6a8d1bcd1bf4d50d6154b6da731b1" - "87b58dfd728afa36757a797ac188d1" - ) - ] - - for tv in tvs: - key = unhexlify(tv[0]) - nonce = unhexlify(tv[1]) - offset = tv[2] * 64 - pt = unhexlify(tv[3]) - ct_expect = unhexlify(tv[4]) - - cipher = ChaCha20.new(key=key, nonce=nonce) - if offset != 0: - cipher.seek(offset) - ct = cipher.encrypt(pt) - assert(ct == ct_expect) - - -class XChaCha20Test(unittest.TestCase): - - # From https://tools.ietf.org/html/draft-arciszewski-xchacha-03 - - def test_hchacha20(self): - # Section 2.2.1 - - from Cryptodome.Cipher.ChaCha20 import _HChaCha20 - - key = b"00:01:02:03:04:05:06:07:08:09:0a:0b:0c:0d:0e:0f:10:11:12:13:14:15:16:17:18:19:1a:1b:1c:1d:1e:1f" - key = unhexlify(key.replace(b":", b"")) - - nonce = b"00:00:00:09:00:00:00:4a:00:00:00:00:31:41:59:27" - nonce = unhexlify(nonce.replace(b":", b"")) - - subkey = _HChaCha20(key, nonce) - - expected = b"82413b42 27b27bfe d30e4250 8a877d73 a0f9e4d5 8a74a853 c12ec413 26d3ecdc" - expected = unhexlify(expected.replace(b" ", b"")) - - self.assertEqual(subkey, expected) - - def test_nonce(self): - key = b'A' * 32 - nonce = b'P' * 24 - cipher = ChaCha20.new(key=key, nonce=nonce) - self.assertEqual(nonce, cipher.nonce) - - def test_encrypt(self): - # Section A.3.2 - - pt = b""" - 5468652064686f6c65202870726f6e6f756e6365642022646f6c652229206973 - 20616c736f206b6e6f776e2061732074686520417369617469632077696c6420 - 646f672c2072656420646f672c20616e642077686973746c696e6720646f672e - 2049742069732061626f7574207468652073697a65206f662061204765726d61 - 6e20736865706865726420627574206c6f6f6b73206d6f7265206c696b652061 - 206c6f6e672d6c656767656420666f782e205468697320686967686c7920656c - 757369766520616e6420736b696c6c6564206a756d70657220697320636c6173 - 736966696564207769746820776f6c7665732c20636f796f7465732c206a6163 - 6b616c732c20616e6420666f78657320696e20746865207461786f6e6f6d6963 - 2066616d696c792043616e696461652e""" - pt = unhexlify(pt.replace(b"\n", b"").replace(b" ", b"")) - - key = unhexlify(b"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f") - iv = unhexlify(b"404142434445464748494a4b4c4d4e4f5051525354555658") - - ct = b""" - 7d0a2e6b7f7c65a236542630294e063b7ab9b555a5d5149aa21e4ae1e4fbce87 - ecc8e08a8b5e350abe622b2ffa617b202cfad72032a3037e76ffdcdc4376ee05 - 3a190d7e46ca1de04144850381b9cb29f051915386b8a710b8ac4d027b8b050f - 7cba5854e028d564e453b8a968824173fc16488b8970cac828f11ae53cabd201 - 12f87107df24ee6183d2274fe4c8b1485534ef2c5fbc1ec24bfc3663efaa08bc - 047d29d25043532db8391a8a3d776bf4372a6955827ccb0cdd4af403a7ce4c63 - d595c75a43e045f0cce1f29c8b93bd65afc5974922f214a40b7c402cdb91ae73 - c0b63615cdad0480680f16515a7ace9d39236464328a37743ffc28f4ddb324f4 - d0f5bbdc270c65b1749a6efff1fbaa09536175ccd29fb9e6057b307320d31683 - 8a9c71f70b5b5907a66f7ea49aadc409""" - ct = unhexlify(ct.replace(b"\n", b"").replace(b" ", b"")) - - cipher = ChaCha20.new(key=key, nonce=iv) - cipher.seek(64) # Counter = 1 - ct_test = cipher.encrypt(pt) - self.assertEqual(ct, ct_test) - - -class ByteArrayTest(unittest.TestCase): - """Verify we can encrypt or decrypt bytearrays""" - - def runTest(self): - - data = b"0123" - key = b"9" * 32 - nonce = b"t" * 8 - - # Encryption - data_ba = bytearray(data) - key_ba = bytearray(key) - nonce_ba = bytearray(nonce) - - cipher1 = ChaCha20.new(key=key, nonce=nonce) - ct = cipher1.encrypt(data) - - cipher2 = ChaCha20.new(key=key_ba, nonce=nonce_ba) - key_ba[:1] = b'\xFF' - nonce_ba[:1] = b'\xFF' - ct_test = cipher2.encrypt(data_ba) - - self.assertEqual(ct, ct_test) - self.assertEqual(cipher1.nonce, cipher2.nonce) - - # Decryption - key_ba = bytearray(key) - nonce_ba = bytearray(nonce) - ct_ba = bytearray(ct) - - cipher3 = ChaCha20.new(key=key_ba, nonce=nonce_ba) - key_ba[:1] = b'\xFF' - nonce_ba[:1] = b'\xFF' - pt_test = cipher3.decrypt(ct_ba) - - self.assertEqual(data, pt_test) - - -class MemoryviewTest(unittest.TestCase): - """Verify we can encrypt or decrypt bytearrays""" - - def runTest(self): - - data = b"0123" - key = b"9" * 32 - nonce = b"t" * 8 - - # Encryption - data_mv = memoryview(bytearray(data)) - key_mv = memoryview(bytearray(key)) - nonce_mv = memoryview(bytearray(nonce)) - - cipher1 = ChaCha20.new(key=key, nonce=nonce) - ct = cipher1.encrypt(data) - - cipher2 = ChaCha20.new(key=key_mv, nonce=nonce_mv) - key_mv[:1] = b'\xFF' - nonce_mv[:1] = b'\xFF' - ct_test = cipher2.encrypt(data_mv) - - self.assertEqual(ct, ct_test) - self.assertEqual(cipher1.nonce, cipher2.nonce) - - # Decryption - key_mv = memoryview(bytearray(key)) - nonce_mv = memoryview(bytearray(nonce)) - ct_mv = memoryview(bytearray(ct)) - - cipher3 = ChaCha20.new(key=key_mv, nonce=nonce_mv) - key_mv[:1] = b'\xFF' - nonce_mv[:1] = b'\xFF' - pt_test = cipher3.decrypt(ct_mv) - - self.assertEqual(data, pt_test) - - -class ChaCha20_AGL_NIR(unittest.TestCase): - - # From http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04 - # and http://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04 - tv = [ - ( "00" * 32, - "00" * 8, - "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc" - "8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11c" - "c387b669b2ee6586" - "9f07e7be5551387a98ba977c732d080d" - "cb0f29a048e3656912c6533e32ee7aed" - "29b721769ce64e43d57133b074d839d5" - "31ed1f28510afb45ace10a1f4b794d6f" - ), - ( "00" * 31 + "01", - "00" * 8, - "4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952" - "ed432d41bbe2a0b6ea7566d2a5d1e7e20d42af2c53d792b1c43fea81" - "7e9ad275ae546963" - "3aeb5224ecf849929b9d828db1ced4dd" - "832025e8018b8160b82284f3c949aa5a" - "8eca00bbb4a73bdad192b5c42f73f2fd" - "4e273644c8b36125a64addeb006c13a0" - ), - ( "00" * 32, - "00" * 7 + "01", - "de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df1" - "37821031e85a050278a7084527214f73efc7fa5b5277062eb7a0433e" - "445f41e3" - ), - ( "00" * 32, - "01" + "00" * 7, - "ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd1" - "38e50d32111e4caf237ee53ca8ad6426194a88545ddc497a0b466e7d" - "6bbdb0041b2f586b" - ), - ( "000102030405060708090a0b0c0d0e0f101112131415161718191a1b" - "1c1d1e1f", - "0001020304050607", - "f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56" - "f85ac3c134a4547b733b46413042c9440049176905d3be59ea1c53f1" - "5916155c2be8241a38008b9a26bc35941e2444177c8ade6689de9526" - "4986d95889fb60e84629c9bd9a5acb1cc118be563eb9b3a4a472f82e" - "09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a4750" - "32b63fc385245fe054e3dd5a97a5f576fe064025d3ce042c566ab2c5" - "07b138db853e3d6959660996546cc9c4a6eafdc777c040d70eaf46f7" - "6dad3979e5c5360c3317166a1c894c94a371876a94df7628fe4eaaf2" - "ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb3ab7" - "8fab78c9" - ), - ( "00" * 32, - "00" * 7 + "02", - "c2c64d378cd536374ae204b9ef933fcd" - "1a8b2288b3dfa49672ab765b54ee27c7" - "8a970e0e955c14f3a88e741b97c286f7" - "5f8fc299e8148362fa198a39531bed6d" - ), - ] - - def runTest(self): - for (key, nonce, stream) in self.tv: - c = ChaCha20.new(key=unhexlify(b(key)), nonce=unhexlify(b(nonce))) - ct = unhexlify(b(stream)) - pt = b("\x00") * len(ct) - self.assertEqual(c.encrypt(pt), ct) - - -class TestOutput(unittest.TestCase): - - def runTest(self): - # Encrypt/Decrypt data and test output parameter - - key = b'4' * 32 - nonce = b'5' * 8 - cipher = ChaCha20.new(key=key, nonce=nonce) - - pt = b'5' * 300 - ct = cipher.encrypt(pt) - - output = bytearray(len(pt)) - cipher = ChaCha20.new(key=key, nonce=nonce) - res = cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - self.assertEqual(res, None) - - cipher = ChaCha20.new(key=key, nonce=nonce) - res = cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - self.assertEqual(res, None) - - output = memoryview(bytearray(len(pt))) - cipher = ChaCha20.new(key=key, nonce=nonce) - cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - - cipher = ChaCha20.new(key=key, nonce=nonce) - cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - - cipher = ChaCha20.new(key=key, nonce=nonce) - self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0'*len(pt)) - - cipher = ChaCha20.new(key=key, nonce=nonce) - self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0'*len(pt)) - - shorter_output = bytearray(len(pt) - 1) - - cipher = ChaCha20.new(key=key, nonce=nonce) - self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output) - - cipher = ChaCha20.new(key=key, nonce=nonce) - self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(ChaCha20Test) - tests += list_test_cases(XChaCha20Test) - tests.append(ChaCha20_AGL_NIR()) - tests.append(ByteArrayTest()) - tests.append(MemoryviewTest()) - tests.append(TestOutput()) - - return tests - - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_ChaCha20_Poly1305.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_ChaCha20_Poly1305.py deleted file mode 100644 index 495028a..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_ChaCha20_Poly1305.py +++ /dev/null @@ -1,776 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2018, Helder Eijs -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import unittest -from binascii import unhexlify - -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.SelfTest.loader import load_test_vectors_wycheproof -from Cryptodome.Util.py3compat import tobytes -from Cryptodome.Cipher import ChaCha20_Poly1305 -from Cryptodome.Hash import SHAKE128 - -from Cryptodome.Util.strxor import strxor - - -def get_tag_random(tag, length): - return SHAKE128.new(data=tobytes(tag)).read(length) - - -class ChaCha20Poly1305Tests(unittest.TestCase): - - key_256 = get_tag_random("key_256", 32) - nonce_96 = get_tag_random("nonce_96", 12) - data_128 = get_tag_random("data_128", 16) - - def test_loopback(self): - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - pt = get_tag_random("plaintext", 16 * 100) - ct = cipher.encrypt(pt) - - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - pt2 = cipher.decrypt(ct) - self.assertEqual(pt, pt2) - - def test_nonce(self): - # Nonce can only be 8 or 12 bytes - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=b'H' * 8) - self.assertEqual(len(cipher.nonce), 8) - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=b'H' * 12) - self.assertEqual(len(cipher.nonce), 12) - - # If not passed, the nonce is created randomly - cipher = ChaCha20_Poly1305.new(key=self.key_256) - nonce1 = cipher.nonce - cipher = ChaCha20_Poly1305.new(key=self.key_256) - nonce2 = cipher.nonce - self.assertEqual(len(nonce1), 12) - self.assertNotEqual(nonce1, nonce2) - - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - ct = cipher.encrypt(self.data_128) - - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - self.assertEqual(ct, cipher.encrypt(self.data_128)) - - def test_nonce_must_be_bytes(self): - self.assertRaises(TypeError, - ChaCha20_Poly1305.new, - key=self.key_256, - nonce=u'test12345678') - - def test_nonce_length(self): - # nonce can only be 8 or 12 bytes long - self.assertRaises(ValueError, - ChaCha20_Poly1305.new, - key=self.key_256, - nonce=b'0' * 7) - self.assertRaises(ValueError, - ChaCha20_Poly1305.new, - key=self.key_256, - nonce=b'') - - def test_block_size(self): - # Not based on block ciphers - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - self.assertFalse(hasattr(cipher, 'block_size')) - - def test_nonce_attribute(self): - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - self.assertEqual(cipher.nonce, self.nonce_96) - - # By default, a 12 bytes long nonce is randomly generated - nonce1 = ChaCha20_Poly1305.new(key=self.key_256).nonce - nonce2 = ChaCha20_Poly1305.new(key=self.key_256).nonce - self.assertEqual(len(nonce1), 12) - self.assertNotEqual(nonce1, nonce2) - - def test_unknown_parameters(self): - self.assertRaises(TypeError, - ChaCha20_Poly1305.new, - key=self.key_256, - param=9) - - def test_null_encryption_decryption(self): - for func in "encrypt", "decrypt": - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - result = getattr(cipher, func)(b"") - self.assertEqual(result, b"") - - def test_either_encrypt_or_decrypt(self): - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.encrypt(b"") - self.assertRaises(TypeError, cipher.decrypt, b"") - - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.decrypt(b"") - self.assertRaises(TypeError, cipher.encrypt, b"") - - def test_data_must_be_bytes(self): - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.encrypt, u'test1234567890-*') - - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.decrypt, u'test1234567890-*') - - def test_mac_len(self): - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - _, mac = cipher.encrypt_and_digest(self.data_128) - self.assertEqual(len(mac), 16) - - def test_invalid_mac(self): - from Cryptodome.Util.strxor import strxor_c - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - ct, mac = cipher.encrypt_and_digest(self.data_128) - - invalid_mac = strxor_c(mac, 0x01) - - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - self.assertRaises(ValueError, cipher.decrypt_and_verify, ct, - invalid_mac) - - def test_hex_mac(self): - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - mac_hex = cipher.hexdigest() - self.assertEqual(cipher.digest(), unhexlify(mac_hex)) - - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.hexverify(mac_hex) - - def test_message_chunks(self): - # Validate that both associated data and plaintext/ciphertext - # can be broken up in chunks of arbitrary length - - auth_data = get_tag_random("authenticated data", 127) - plaintext = get_tag_random("plaintext", 127) - - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.update(auth_data) - ciphertext, ref_mac = cipher.encrypt_and_digest(plaintext) - - def break_up(data, chunk_length): - return [data[i:i+chunk_length] for i in range(0, len(data), - chunk_length)] - - # Encryption - for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128: - - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - - for chunk in break_up(auth_data, chunk_length): - cipher.update(chunk) - pt2 = b"" - for chunk in break_up(ciphertext, chunk_length): - pt2 += cipher.decrypt(chunk) - self.assertEqual(plaintext, pt2) - cipher.verify(ref_mac) - - # Decryption - for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128: - - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - - for chunk in break_up(auth_data, chunk_length): - cipher.update(chunk) - ct2 = b"" - for chunk in break_up(plaintext, chunk_length): - ct2 += cipher.encrypt(chunk) - self.assertEqual(ciphertext, ct2) - self.assertEqual(cipher.digest(), ref_mac) - - def test_bytearray(self): - - # Encrypt - key_ba = bytearray(self.key_256) - nonce_ba = bytearray(self.nonce_96) - header_ba = bytearray(self.data_128) - data_ba = bytearray(self.data_128) - - cipher1 = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher1.update(self.data_128) - ct = cipher1.encrypt(self.data_128) - tag = cipher1.digest() - - cipher2 = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - key_ba[:3] = b'\xFF\xFF\xFF' - nonce_ba[:3] = b'\xFF\xFF\xFF' - cipher2.update(header_ba) - header_ba[:3] = b'\xFF\xFF\xFF' - ct_test = cipher2.encrypt(data_ba) - data_ba[:3] = b'\x99\x99\x99' - tag_test = cipher2.digest() - - self.assertEqual(ct, ct_test) - self.assertEqual(tag, tag_test) - self.assertEqual(cipher1.nonce, cipher2.nonce) - - # Decrypt - key_ba = bytearray(self.key_256) - nonce_ba = bytearray(self.nonce_96) - header_ba = bytearray(self.data_128) - ct_ba = bytearray(ct) - tag_ba = bytearray(tag) - del data_ba - - cipher3 = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - key_ba[:3] = b'\xFF\xFF\xFF' - nonce_ba[:3] = b'\xFF\xFF\xFF' - cipher3.update(header_ba) - header_ba[:3] = b'\xFF\xFF\xFF' - pt_test = cipher3.decrypt(ct_ba) - ct_ba[:3] = b'\xFF\xFF\xFF' - cipher3.verify(tag_ba) - - self.assertEqual(pt_test, self.data_128) - - def test_memoryview(self): - - # Encrypt - key_mv = memoryview(bytearray(self.key_256)) - nonce_mv = memoryview(bytearray(self.nonce_96)) - header_mv = memoryview(bytearray(self.data_128)) - data_mv = memoryview(bytearray(self.data_128)) - - cipher1 = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher1.update(self.data_128) - ct = cipher1.encrypt(self.data_128) - tag = cipher1.digest() - - cipher2 = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - key_mv[:3] = b'\xFF\xFF\xFF' - nonce_mv[:3] = b'\xFF\xFF\xFF' - cipher2.update(header_mv) - header_mv[:3] = b'\xFF\xFF\xFF' - ct_test = cipher2.encrypt(data_mv) - data_mv[:3] = b'\x99\x99\x99' - tag_test = cipher2.digest() - - self.assertEqual(ct, ct_test) - self.assertEqual(tag, tag_test) - self.assertEqual(cipher1.nonce, cipher2.nonce) - - # Decrypt - key_mv = memoryview(bytearray(self.key_256)) - nonce_mv = memoryview(bytearray(self.nonce_96)) - header_mv = memoryview(bytearray(self.data_128)) - ct_mv = memoryview(bytearray(ct)) - tag_mv = memoryview(bytearray(tag)) - del data_mv - - cipher3 = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - key_mv[:3] = b'\xFF\xFF\xFF' - nonce_mv[:3] = b'\xFF\xFF\xFF' - cipher3.update(header_mv) - header_mv[:3] = b'\xFF\xFF\xFF' - pt_test = cipher3.decrypt(ct_mv) - ct_mv[:3] = b'\x99\x99\x99' - cipher3.verify(tag_mv) - - self.assertEqual(pt_test, self.data_128) - - -class XChaCha20Poly1305Tests(unittest.TestCase): - - def test_nonce(self): - # Nonce can only be 24 bytes - cipher = ChaCha20_Poly1305.new(key=b'Y' * 32, - nonce=b'H' * 24) - self.assertEqual(len(cipher.nonce), 24) - self.assertEqual(cipher.nonce, b'H' * 24) - - def test_encrypt(self): - # From https://tools.ietf.org/html/draft-arciszewski-xchacha-03 - # Section A.3.1 - - pt = b""" - 4c616469657320616e642047656e746c656d656e206f662074686520636c6173 - 73206f66202739393a204966204920636f756c64206f6666657220796f75206f - 6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73 - 637265656e20776f756c642062652069742e""" - pt = unhexlify(pt.replace(b"\n", b"").replace(b" ", b"")) - - aad = unhexlify(b"50515253c0c1c2c3c4c5c6c7") - key = unhexlify(b"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f") - iv = unhexlify(b"404142434445464748494a4b4c4d4e4f5051525354555657") - - ct = b""" - bd6d179d3e83d43b9576579493c0e939572a1700252bfaccbed2902c21396cbb - 731c7f1b0b4aa6440bf3a82f4eda7e39ae64c6708c54c216cb96b72e1213b452 - 2f8c9ba40db5d945b11b69b982c1bb9e3f3fac2bc369488f76b2383565d3fff9 - 21f9664c97637da9768812f615c68b13b52e""" - ct = unhexlify(ct.replace(b"\n", b"").replace(b" ", b"")) - - tag = unhexlify(b"c0875924c1c7987947deafd8780acf49") - - cipher = ChaCha20_Poly1305.new(key=key, nonce=iv) - cipher.update(aad) - ct_test, tag_test = cipher.encrypt_and_digest(pt) - - self.assertEqual(ct, ct_test) - self.assertEqual(tag, tag_test) - - cipher = ChaCha20_Poly1305.new(key=key, nonce=iv) - cipher.update(aad) - cipher.decrypt_and_verify(ct, tag) - - -class ChaCha20Poly1305FSMTests(unittest.TestCase): - - key_256 = get_tag_random("key_256", 32) - nonce_96 = get_tag_random("nonce_96", 12) - data_128 = get_tag_random("data_128", 16) - - def test_valid_init_encrypt_decrypt_digest_verify(self): - # No authenticated data, fixed plaintext - # Verify path INIT->ENCRYPT->DIGEST - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - ct = cipher.encrypt(self.data_128) - mac = cipher.digest() - - # Verify path INIT->DECRYPT->VERIFY - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.decrypt(ct) - cipher.verify(mac) - - def test_valid_init_update_digest_verify(self): - # No plaintext, fixed authenticated data - # Verify path INIT->UPDATE->DIGEST - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.update(self.data_128) - mac = cipher.digest() - - # Verify path INIT->UPDATE->VERIFY - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.update(self.data_128) - cipher.verify(mac) - - def test_valid_full_path(self): - # Fixed authenticated data, fixed plaintext - # Verify path INIT->UPDATE->ENCRYPT->DIGEST - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.update(self.data_128) - ct = cipher.encrypt(self.data_128) - mac = cipher.digest() - - # Verify path INIT->UPDATE->DECRYPT->VERIFY - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.update(self.data_128) - cipher.decrypt(ct) - cipher.verify(mac) - - def test_valid_init_digest(self): - # Verify path INIT->DIGEST - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.digest() - - def test_valid_init_verify(self): - # Verify path INIT->VERIFY - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - mac = cipher.digest() - - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.verify(mac) - - def test_valid_multiple_encrypt_or_decrypt(self): - for method_name in "encrypt", "decrypt": - for auth_data in (None, b"333", self.data_128, - self.data_128 + b"3"): - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - if auth_data is not None: - cipher.update(auth_data) - method = getattr(cipher, method_name) - method(self.data_128) - method(self.data_128) - method(self.data_128) - method(self.data_128) - - def test_valid_multiple_digest_or_verify(self): - # Multiple calls to digest - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.update(self.data_128) - first_mac = cipher.digest() - for x in range(4): - self.assertEqual(first_mac, cipher.digest()) - - # Multiple calls to verify - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.update(self.data_128) - for x in range(5): - cipher.verify(first_mac) - - def test_valid_encrypt_and_digest_decrypt_and_verify(self): - # encrypt_and_digest - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.update(self.data_128) - ct, mac = cipher.encrypt_and_digest(self.data_128) - - # decrypt_and_verify - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.update(self.data_128) - pt = cipher.decrypt_and_verify(ct, mac) - self.assertEqual(self.data_128, pt) - - def test_invalid_mixing_encrypt_decrypt(self): - # Once per method, with or without assoc. data - for method1_name, method2_name in (("encrypt", "decrypt"), - ("decrypt", "encrypt")): - for assoc_data_present in (True, False): - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - if assoc_data_present: - cipher.update(self.data_128) - getattr(cipher, method1_name)(self.data_128) - self.assertRaises(TypeError, getattr(cipher, method2_name), - self.data_128) - - def test_invalid_encrypt_or_update_after_digest(self): - for method_name in "encrypt", "update": - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.encrypt(self.data_128) - cipher.digest() - self.assertRaises(TypeError, getattr(cipher, method_name), - self.data_128) - - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.encrypt_and_digest(self.data_128) - - def test_invalid_decrypt_or_update_after_verify(self): - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - ct = cipher.encrypt(self.data_128) - mac = cipher.digest() - - for method_name in "decrypt", "update": - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.decrypt(ct) - cipher.verify(mac) - self.assertRaises(TypeError, getattr(cipher, method_name), - self.data_128) - - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.decrypt(ct) - cipher.verify(mac) - self.assertRaises(TypeError, getattr(cipher, method_name), - self.data_128) - - cipher = ChaCha20_Poly1305.new(key=self.key_256, - nonce=self.nonce_96) - cipher.decrypt_and_verify(ct, mac) - self.assertRaises(TypeError, getattr(cipher, method_name), - self.data_128) - - -def compact(x): - return unhexlify(x.replace(" ", "").replace(":", "")) - - -class TestVectorsRFC(unittest.TestCase): - """Test cases from RFC7539""" - - # AAD, PT, CT, MAC, KEY, NONCE - test_vectors_hex = [ - ( '50 51 52 53 c0 c1 c2 c3 c4 c5 c6 c7', - '4c 61 64 69 65 73 20 61 6e 64 20 47 65 6e 74 6c' - '65 6d 65 6e 20 6f 66 20 74 68 65 20 63 6c 61 73' - '73 20 6f 66 20 27 39 39 3a 20 49 66 20 49 20 63' - '6f 75 6c 64 20 6f 66 66 65 72 20 79 6f 75 20 6f' - '6e 6c 79 20 6f 6e 65 20 74 69 70 20 66 6f 72 20' - '74 68 65 20 66 75 74 75 72 65 2c 20 73 75 6e 73' - '63 72 65 65 6e 20 77 6f 75 6c 64 20 62 65 20 69' - '74 2e', - 'd3 1a 8d 34 64 8e 60 db 7b 86 af bc 53 ef 7e c2' - 'a4 ad ed 51 29 6e 08 fe a9 e2 b5 a7 36 ee 62 d6' - '3d be a4 5e 8c a9 67 12 82 fa fb 69 da 92 72 8b' - '1a 71 de 0a 9e 06 0b 29 05 d6 a5 b6 7e cd 3b 36' - '92 dd bd 7f 2d 77 8b 8c 98 03 ae e3 28 09 1b 58' - 'fa b3 24 e4 fa d6 75 94 55 85 80 8b 48 31 d7 bc' - '3f f4 de f0 8e 4b 7a 9d e5 76 d2 65 86 ce c6 4b' - '61 16', - '1a:e1:0b:59:4f:09:e2:6a:7e:90:2e:cb:d0:60:06:91', - '80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f' - '90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f', - '07 00 00 00' + '40 41 42 43 44 45 46 47', - ), - ( 'f3 33 88 86 00 00 00 00 00 00 4e 91', - '49 6e 74 65 72 6e 65 74 2d 44 72 61 66 74 73 20' - '61 72 65 20 64 72 61 66 74 20 64 6f 63 75 6d 65' - '6e 74 73 20 76 61 6c 69 64 20 66 6f 72 20 61 20' - '6d 61 78 69 6d 75 6d 20 6f 66 20 73 69 78 20 6d' - '6f 6e 74 68 73 20 61 6e 64 20 6d 61 79 20 62 65' - '20 75 70 64 61 74 65 64 2c 20 72 65 70 6c 61 63' - '65 64 2c 20 6f 72 20 6f 62 73 6f 6c 65 74 65 64' - '20 62 79 20 6f 74 68 65 72 20 64 6f 63 75 6d 65' - '6e 74 73 20 61 74 20 61 6e 79 20 74 69 6d 65 2e' - '20 49 74 20 69 73 20 69 6e 61 70 70 72 6f 70 72' - '69 61 74 65 20 74 6f 20 75 73 65 20 49 6e 74 65' - '72 6e 65 74 2d 44 72 61 66 74 73 20 61 73 20 72' - '65 66 65 72 65 6e 63 65 20 6d 61 74 65 72 69 61' - '6c 20 6f 72 20 74 6f 20 63 69 74 65 20 74 68 65' - '6d 20 6f 74 68 65 72 20 74 68 61 6e 20 61 73 20' - '2f e2 80 9c 77 6f 72 6b 20 69 6e 20 70 72 6f 67' - '72 65 73 73 2e 2f e2 80 9d', - '64 a0 86 15 75 86 1a f4 60 f0 62 c7 9b e6 43 bd' - '5e 80 5c fd 34 5c f3 89 f1 08 67 0a c7 6c 8c b2' - '4c 6c fc 18 75 5d 43 ee a0 9e e9 4e 38 2d 26 b0' - 'bd b7 b7 3c 32 1b 01 00 d4 f0 3b 7f 35 58 94 cf' - '33 2f 83 0e 71 0b 97 ce 98 c8 a8 4a bd 0b 94 81' - '14 ad 17 6e 00 8d 33 bd 60 f9 82 b1 ff 37 c8 55' - '97 97 a0 6e f4 f0 ef 61 c1 86 32 4e 2b 35 06 38' - '36 06 90 7b 6a 7c 02 b0 f9 f6 15 7b 53 c8 67 e4' - 'b9 16 6c 76 7b 80 4d 46 a5 9b 52 16 cd e7 a4 e9' - '90 40 c5 a4 04 33 22 5e e2 82 a1 b0 a0 6c 52 3e' - 'af 45 34 d7 f8 3f a1 15 5b 00 47 71 8c bc 54 6a' - '0d 07 2b 04 b3 56 4e ea 1b 42 22 73 f5 48 27 1a' - '0b b2 31 60 53 fa 76 99 19 55 eb d6 31 59 43 4e' - 'ce bb 4e 46 6d ae 5a 10 73 a6 72 76 27 09 7a 10' - '49 e6 17 d9 1d 36 10 94 fa 68 f0 ff 77 98 71 30' - '30 5b ea ba 2e da 04 df 99 7b 71 4d 6c 6f 2c 29' - 'a6 ad 5c b4 02 2b 02 70 9b', - 'ee ad 9d 67 89 0c bb 22 39 23 36 fe a1 85 1f 38', - '1c 92 40 a5 eb 55 d3 8a f3 33 88 86 04 f6 b5 f0' - '47 39 17 c1 40 2b 80 09 9d ca 5c bc 20 70 75 c0', - '00 00 00 00 01 02 03 04 05 06 07 08', - ) - ] - - test_vectors = [[unhexlify(x.replace(" ", "").replace(":", "")) for x in tv] for tv in test_vectors_hex] - - def runTest(self): - for assoc_data, pt, ct, mac, key, nonce in self.test_vectors: - # Encrypt - cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce) - cipher.update(assoc_data) - ct2, mac2 = cipher.encrypt_and_digest(pt) - self.assertEqual(ct, ct2) - self.assertEqual(mac, mac2) - - # Decrypt - cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce) - cipher.update(assoc_data) - pt2 = cipher.decrypt_and_verify(ct, mac) - self.assertEqual(pt, pt2) - - -class TestVectorsWycheproof(unittest.TestCase): - - def __init__(self, wycheproof_warnings): - unittest.TestCase.__init__(self) - self._wycheproof_warnings = wycheproof_warnings - self._id = "None" - - def load_tests(self, filename): - - def filter_tag(group): - return group['tagSize'] // 8 - - def filter_algo(root): - return root['algorithm'] - - result = load_test_vectors_wycheproof(("Cipher", "wycheproof"), - filename, - "Wycheproof ChaCha20-Poly1305", - root_tag={'algo': filter_algo}, - group_tag={'tag_size': filter_tag}) - return result - - def setUp(self): - self.tv = [] - self.tv.extend(self.load_tests("chacha20_poly1305_test.json")) - self.tv.extend(self.load_tests("xchacha20_poly1305_test.json")) - - def shortDescription(self): - return self._id - - def warn(self, tv): - if tv.warning and self._wycheproof_warnings: - import warnings - warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment)) - - def test_encrypt(self, tv): - self._id = "Wycheproof Encrypt %s Test #%s" % (tv.algo, tv.id) - - try: - cipher = ChaCha20_Poly1305.new(key=tv.key, nonce=tv.iv) - except ValueError as e: - assert len(tv.iv) not in (8, 12) and "Nonce must be" in str(e) - return - - cipher.update(tv.aad) - ct, tag = cipher.encrypt_and_digest(tv.msg) - if tv.valid: - self.assertEqual(ct, tv.ct) - self.assertEqual(tag, tv.tag) - self.warn(tv) - - def test_decrypt(self, tv): - self._id = "Wycheproof Decrypt %s Test #%s" % (tv.algo, tv.id) - - try: - cipher = ChaCha20_Poly1305.new(key=tv.key, nonce=tv.iv) - except ValueError as e: - assert len(tv.iv) not in (8, 12) and "Nonce must be" in str(e) - return - - cipher.update(tv.aad) - try: - pt = cipher.decrypt_and_verify(tv.ct, tv.tag) - except ValueError: - assert not tv.valid - else: - assert tv.valid - self.assertEqual(pt, tv.msg) - self.warn(tv) - - def test_corrupt_decrypt(self, tv): - self._id = "Wycheproof Corrupt Decrypt ChaCha20-Poly1305 Test #" + str(tv.id) - if len(tv.iv) == 0 or len(tv.ct) < 1: - return - cipher = ChaCha20_Poly1305.new(key=tv.key, nonce=tv.iv) - cipher.update(tv.aad) - ct_corrupt = strxor(tv.ct, b"\x00" * (len(tv.ct) - 1) + b"\x01") - self.assertRaises(ValueError, cipher.decrypt_and_verify, ct_corrupt, tv.tag) - - def runTest(self): - - for tv in self.tv: - self.test_encrypt(tv) - self.test_decrypt(tv) - self.test_corrupt_decrypt(tv) - - -class TestOutput(unittest.TestCase): - - def runTest(self): - # Encrypt/Decrypt data and test output parameter - - key = b'4' * 32 - nonce = b'5' * 12 - cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce) - - pt = b'5' * 16 - ct = cipher.encrypt(pt) - - output = bytearray(16) - cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce) - res = cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - self.assertEqual(res, None) - - cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce) - res = cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - self.assertEqual(res, None) - - output = memoryview(bytearray(16)) - cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce) - cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - - cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce) - cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - - cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce) - self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0'*16) - - cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce) - self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0'*16) - - shorter_output = bytearray(7) - - cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce) - self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output) - - cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce) - self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output) - - -def get_tests(config={}): - wycheproof_warnings = config.get('wycheproof_warnings') - - tests = [] - tests += list_test_cases(ChaCha20Poly1305Tests) - tests += list_test_cases(XChaCha20Poly1305Tests) - tests += list_test_cases(ChaCha20Poly1305FSMTests) - tests += [TestVectorsRFC()] - tests += [TestVectorsWycheproof(wycheproof_warnings)] - tests += [TestOutput()] - return tests - - -if __name__ == '__main__': - def suite(): - unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_DES.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_DES.py deleted file mode 100644 index df1313a..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_DES.py +++ /dev/null @@ -1,374 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Cipher/DES.py: Self-test for the (Single) DES cipher -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Cipher.DES""" - -import unittest - -from Cryptodome.Cipher import DES - -# This is a list of (plaintext, ciphertext, key, description) tuples. -SP800_17_B1_KEY = '01' * 8 -SP800_17_B2_PT = '00' * 8 -test_data = [ - # Test vectors from Appendix A of NIST SP 800-17 - # "Modes of Operation Validation System (MOVS): Requirements and Procedures" - # http://csrc.nist.gov/publications/nistpubs/800-17/800-17.pdf - - # Appendix A - "Sample Round Outputs for the DES" - ('0000000000000000', '82dcbafbdeab6602', '10316e028c8f3b4a', - "NIST SP800-17 A"), - - # Table B.1 - Variable Plaintext Known Answer Test - ('8000000000000000', '95f8a5e5dd31d900', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #0'), - ('4000000000000000', 'dd7f121ca5015619', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #1'), - ('2000000000000000', '2e8653104f3834ea', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #2'), - ('1000000000000000', '4bd388ff6cd81d4f', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #3'), - ('0800000000000000', '20b9e767b2fb1456', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #4'), - ('0400000000000000', '55579380d77138ef', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #5'), - ('0200000000000000', '6cc5defaaf04512f', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #6'), - ('0100000000000000', '0d9f279ba5d87260', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #7'), - ('0080000000000000', 'd9031b0271bd5a0a', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #8'), - ('0040000000000000', '424250b37c3dd951', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #9'), - ('0020000000000000', 'b8061b7ecd9a21e5', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #10'), - ('0010000000000000', 'f15d0f286b65bd28', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #11'), - ('0008000000000000', 'add0cc8d6e5deba1', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #12'), - ('0004000000000000', 'e6d5f82752ad63d1', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #13'), - ('0002000000000000', 'ecbfe3bd3f591a5e', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #14'), - ('0001000000000000', 'f356834379d165cd', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #15'), - ('0000800000000000', '2b9f982f20037fa9', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #16'), - ('0000400000000000', '889de068a16f0be6', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #17'), - ('0000200000000000', 'e19e275d846a1298', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #18'), - ('0000100000000000', '329a8ed523d71aec', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #19'), - ('0000080000000000', 'e7fce22557d23c97', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #20'), - ('0000040000000000', '12a9f5817ff2d65d', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #21'), - ('0000020000000000', 'a484c3ad38dc9c19', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #22'), - ('0000010000000000', 'fbe00a8a1ef8ad72', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #23'), - ('0000008000000000', '750d079407521363', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #24'), - ('0000004000000000', '64feed9c724c2faf', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #25'), - ('0000002000000000', 'f02b263b328e2b60', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #26'), - ('0000001000000000', '9d64555a9a10b852', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #27'), - ('0000000800000000', 'd106ff0bed5255d7', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #28'), - ('0000000400000000', 'e1652c6b138c64a5', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #29'), - ('0000000200000000', 'e428581186ec8f46', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #30'), - ('0000000100000000', 'aeb5f5ede22d1a36', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #31'), - ('0000000080000000', 'e943d7568aec0c5c', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #32'), - ('0000000040000000', 'df98c8276f54b04b', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #33'), - ('0000000020000000', 'b160e4680f6c696f', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #34'), - ('0000000010000000', 'fa0752b07d9c4ab8', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #35'), - ('0000000008000000', 'ca3a2b036dbc8502', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #36'), - ('0000000004000000', '5e0905517bb59bcf', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #37'), - ('0000000002000000', '814eeb3b91d90726', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #38'), - ('0000000001000000', '4d49db1532919c9f', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #39'), - ('0000000000800000', '25eb5fc3f8cf0621', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #40'), - ('0000000000400000', 'ab6a20c0620d1c6f', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #41'), - ('0000000000200000', '79e90dbc98f92cca', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #42'), - ('0000000000100000', '866ecedd8072bb0e', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #43'), - ('0000000000080000', '8b54536f2f3e64a8', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #44'), - ('0000000000040000', 'ea51d3975595b86b', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #45'), - ('0000000000020000', 'caffc6ac4542de31', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #46'), - ('0000000000010000', '8dd45a2ddf90796c', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #47'), - ('0000000000008000', '1029d55e880ec2d0', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #48'), - ('0000000000004000', '5d86cb23639dbea9', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #49'), - ('0000000000002000', '1d1ca853ae7c0c5f', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #50'), - ('0000000000001000', 'ce332329248f3228', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #51'), - ('0000000000000800', '8405d1abe24fb942', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #52'), - ('0000000000000400', 'e643d78090ca4207', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #53'), - ('0000000000000200', '48221b9937748a23', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #54'), - ('0000000000000100', 'dd7c0bbd61fafd54', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #55'), - ('0000000000000080', '2fbc291a570db5c4', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #56'), - ('0000000000000040', 'e07c30d7e4e26e12', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #57'), - ('0000000000000020', '0953e2258e8e90a1', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #58'), - ('0000000000000010', '5b711bc4ceebf2ee', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #59'), - ('0000000000000008', 'cc083f1e6d9e85f6', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #60'), - ('0000000000000004', 'd2fd8867d50d2dfe', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #61'), - ('0000000000000002', '06e7ea22ce92708f', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #62'), - ('0000000000000001', '166b40b44aba4bd6', SP800_17_B1_KEY, - 'NIST SP800-17 B.1 #63'), - - # Table B.2 - Variable Key Known Answer Test - (SP800_17_B2_PT, '95a8d72813daa94d', '8001010101010101', - 'NIST SP800-17 B.2 #0'), - (SP800_17_B2_PT, '0eec1487dd8c26d5', '4001010101010101', - 'NIST SP800-17 B.2 #1'), - (SP800_17_B2_PT, '7ad16ffb79c45926', '2001010101010101', - 'NIST SP800-17 B.2 #2'), - (SP800_17_B2_PT, 'd3746294ca6a6cf3', '1001010101010101', - 'NIST SP800-17 B.2 #3'), - (SP800_17_B2_PT, '809f5f873c1fd761', '0801010101010101', - 'NIST SP800-17 B.2 #4'), - (SP800_17_B2_PT, 'c02faffec989d1fc', '0401010101010101', - 'NIST SP800-17 B.2 #5'), - (SP800_17_B2_PT, '4615aa1d33e72f10', '0201010101010101', - 'NIST SP800-17 B.2 #6'), - (SP800_17_B2_PT, '2055123350c00858', '0180010101010101', - 'NIST SP800-17 B.2 #7'), - (SP800_17_B2_PT, 'df3b99d6577397c8', '0140010101010101', - 'NIST SP800-17 B.2 #8'), - (SP800_17_B2_PT, '31fe17369b5288c9', '0120010101010101', - 'NIST SP800-17 B.2 #9'), - (SP800_17_B2_PT, 'dfdd3cc64dae1642', '0110010101010101', - 'NIST SP800-17 B.2 #10'), - (SP800_17_B2_PT, '178c83ce2b399d94', '0108010101010101', - 'NIST SP800-17 B.2 #11'), - (SP800_17_B2_PT, '50f636324a9b7f80', '0104010101010101', - 'NIST SP800-17 B.2 #12'), - (SP800_17_B2_PT, 'a8468ee3bc18f06d', '0102010101010101', - 'NIST SP800-17 B.2 #13'), - (SP800_17_B2_PT, 'a2dc9e92fd3cde92', '0101800101010101', - 'NIST SP800-17 B.2 #14'), - (SP800_17_B2_PT, 'cac09f797d031287', '0101400101010101', - 'NIST SP800-17 B.2 #15'), - (SP800_17_B2_PT, '90ba680b22aeb525', '0101200101010101', - 'NIST SP800-17 B.2 #16'), - (SP800_17_B2_PT, 'ce7a24f350e280b6', '0101100101010101', - 'NIST SP800-17 B.2 #17'), - (SP800_17_B2_PT, '882bff0aa01a0b87', '0101080101010101', - 'NIST SP800-17 B.2 #18'), - (SP800_17_B2_PT, '25610288924511c2', '0101040101010101', - 'NIST SP800-17 B.2 #19'), - (SP800_17_B2_PT, 'c71516c29c75d170', '0101020101010101', - 'NIST SP800-17 B.2 #20'), - (SP800_17_B2_PT, '5199c29a52c9f059', '0101018001010101', - 'NIST SP800-17 B.2 #21'), - (SP800_17_B2_PT, 'c22f0a294a71f29f', '0101014001010101', - 'NIST SP800-17 B.2 #22'), - (SP800_17_B2_PT, 'ee371483714c02ea', '0101012001010101', - 'NIST SP800-17 B.2 #23'), - (SP800_17_B2_PT, 'a81fbd448f9e522f', '0101011001010101', - 'NIST SP800-17 B.2 #24'), - (SP800_17_B2_PT, '4f644c92e192dfed', '0101010801010101', - 'NIST SP800-17 B.2 #25'), - (SP800_17_B2_PT, '1afa9a66a6df92ae', '0101010401010101', - 'NIST SP800-17 B.2 #26'), - (SP800_17_B2_PT, 'b3c1cc715cb879d8', '0101010201010101', - 'NIST SP800-17 B.2 #27'), - (SP800_17_B2_PT, '19d032e64ab0bd8b', '0101010180010101', - 'NIST SP800-17 B.2 #28'), - (SP800_17_B2_PT, '3cfaa7a7dc8720dc', '0101010140010101', - 'NIST SP800-17 B.2 #29'), - (SP800_17_B2_PT, 'b7265f7f447ac6f3', '0101010120010101', - 'NIST SP800-17 B.2 #30'), - (SP800_17_B2_PT, '9db73b3c0d163f54', '0101010110010101', - 'NIST SP800-17 B.2 #31'), - (SP800_17_B2_PT, '8181b65babf4a975', '0101010108010101', - 'NIST SP800-17 B.2 #32'), - (SP800_17_B2_PT, '93c9b64042eaa240', '0101010104010101', - 'NIST SP800-17 B.2 #33'), - (SP800_17_B2_PT, '5570530829705592', '0101010102010101', - 'NIST SP800-17 B.2 #34'), - (SP800_17_B2_PT, '8638809e878787a0', '0101010101800101', - 'NIST SP800-17 B.2 #35'), - (SP800_17_B2_PT, '41b9a79af79ac208', '0101010101400101', - 'NIST SP800-17 B.2 #36'), - (SP800_17_B2_PT, '7a9be42f2009a892', '0101010101200101', - 'NIST SP800-17 B.2 #37'), - (SP800_17_B2_PT, '29038d56ba6d2745', '0101010101100101', - 'NIST SP800-17 B.2 #38'), - (SP800_17_B2_PT, '5495c6abf1e5df51', '0101010101080101', - 'NIST SP800-17 B.2 #39'), - (SP800_17_B2_PT, 'ae13dbd561488933', '0101010101040101', - 'NIST SP800-17 B.2 #40'), - (SP800_17_B2_PT, '024d1ffa8904e389', '0101010101020101', - 'NIST SP800-17 B.2 #41'), - (SP800_17_B2_PT, 'd1399712f99bf02e', '0101010101018001', - 'NIST SP800-17 B.2 #42'), - (SP800_17_B2_PT, '14c1d7c1cffec79e', '0101010101014001', - 'NIST SP800-17 B.2 #43'), - (SP800_17_B2_PT, '1de5279dae3bed6f', '0101010101012001', - 'NIST SP800-17 B.2 #44'), - (SP800_17_B2_PT, 'e941a33f85501303', '0101010101011001', - 'NIST SP800-17 B.2 #45'), - (SP800_17_B2_PT, 'da99dbbc9a03f379', '0101010101010801', - 'NIST SP800-17 B.2 #46'), - (SP800_17_B2_PT, 'b7fc92f91d8e92e9', '0101010101010401', - 'NIST SP800-17 B.2 #47'), - (SP800_17_B2_PT, 'ae8e5caa3ca04e85', '0101010101010201', - 'NIST SP800-17 B.2 #48'), - (SP800_17_B2_PT, '9cc62df43b6eed74', '0101010101010180', - 'NIST SP800-17 B.2 #49'), - (SP800_17_B2_PT, 'd863dbb5c59a91a0', '0101010101010140', - 'NIST SP800-17 B.2 #50'), - (SP800_17_B2_PT, 'a1ab2190545b91d7', '0101010101010120', - 'NIST SP800-17 B.2 #51'), - (SP800_17_B2_PT, '0875041e64c570f7', '0101010101010110', - 'NIST SP800-17 B.2 #52'), - (SP800_17_B2_PT, '5a594528bebef1cc', '0101010101010108', - 'NIST SP800-17 B.2 #53'), - (SP800_17_B2_PT, 'fcdb3291de21f0c0', '0101010101010104', - 'NIST SP800-17 B.2 #54'), - (SP800_17_B2_PT, '869efd7f9f265a09', '0101010101010102', - 'NIST SP800-17 B.2 #55'), -] - -class RonRivestTest(unittest.TestCase): - """ Ronald L. Rivest's DES test, see - http://people.csail.mit.edu/rivest/Destest.txt - ABSTRACT - -------- - - We present a simple way to test the correctness of a DES implementation: - Use the recurrence relation: - - X0 = 9474B8E8C73BCA7D (hexadecimal) - - X(i+1) = IF (i is even) THEN E(Xi,Xi) ELSE D(Xi,Xi) - - to compute a sequence of 64-bit values: X0, X1, X2, ..., X16. Here - E(X,K) denotes the DES encryption of X using key K, and D(X,K) denotes - the DES decryption of X using key K. If you obtain - - X16 = 1B1A2DDB4C642438 - - your implementation does not have any of the 36,568 possible single-fault - errors described herein. - """ - def runTest(self): - from binascii import b2a_hex - - X = [] - X[0:] = [b'\x94\x74\xB8\xE8\xC7\x3B\xCA\x7D'] - - for i in range(16): - c = DES.new(X[i],DES.MODE_ECB) - if not (i&1): # (num&1) returns 1 for odd numbers - X[i+1:] = [c.encrypt(X[i])] # even - else: - X[i+1:] = [c.decrypt(X[i])] # odd - - self.assertEqual(b2a_hex(X[16]), - b2a_hex(b'\x1B\x1A\x2D\xDB\x4C\x64\x24\x38')) - - -class TestOutput(unittest.TestCase): - - def runTest(self): - # Encrypt/Decrypt data and test output parameter - - cipher = DES.new(b'4'*8, DES.MODE_ECB) - - pt = b'5' * 8 - ct = cipher.encrypt(pt) - - output = bytearray(8) - res = cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - self.assertEqual(res, None) - - res = cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - self.assertEqual(res, None) - - output = memoryview(bytearray(8)) - cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - - cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - - self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0'*8) - self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0'*8) - - shorter_output = bytearray(7) - self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output) - self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output) - - -def get_tests(config={}): - from .common import make_block_tests - tests = make_block_tests(DES, "DES", test_data) - tests += [RonRivestTest()] - tests += [TestOutput()] - return tests - - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_DES3.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_DES3.py deleted file mode 100644 index 8f8479b..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_DES3.py +++ /dev/null @@ -1,195 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Cipher/DES3.py: Self-test for the Triple-DES cipher -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Cipher.DES3""" - -import unittest -from binascii import hexlify, unhexlify - -from Cryptodome.Cipher import DES3 - -from Cryptodome.Util.strxor import strxor_c -from Cryptodome.Util.py3compat import bchr, tostr -from Cryptodome.SelfTest.loader import load_test_vectors -from Cryptodome.SelfTest.st_common import list_test_cases - -# This is a list of (plaintext, ciphertext, key, description) tuples. -test_data = [ - # Test vector from Appendix B of NIST SP 800-67 - # "Recommendation for the Triple Data Encryption Algorithm (TDEA) Block - # Cipher" - # http://csrc.nist.gov/publications/nistpubs/800-67/SP800-67.pdf - ('54686520717566636b2062726f776e20666f78206a756d70', - 'a826fd8ce53b855fcce21c8112256fe668d5c05dd9b6b900', - '0123456789abcdef23456789abcdef01456789abcdef0123', - 'NIST SP800-67 B.1'), - - # This test is designed to test the DES3 API, not the correctness of the - # output. - ('21e81b7ade88a259', '5c577d4d9b20c0f8', - '9b397ebf81b1181e282f4bb8adbadc6b', 'Two-key 3DES'), -] - -# NIST CAVP test vectors - -nist_tdes_mmt_files = ("TECBMMT2.rsp", "TECBMMT3.rsp") - -for tdes_file in nist_tdes_mmt_files: - - test_vectors = load_test_vectors( - ("Cipher", "TDES"), - tdes_file, - "TDES ECB (%s)" % tdes_file, - {"count": lambda x: int(x)}) or [] - - for index, tv in enumerate(test_vectors): - - # The test vector file contains some directive lines - if isinstance(tv, str): - continue - - key = tv.key1 + tv.key2 + tv.key3 - test_data_item = (tostr(hexlify(tv.plaintext)), - tostr(hexlify(tv.ciphertext)), - tostr(hexlify(key)), - "%s (%s)" % (tdes_file, index)) - test_data.append(test_data_item) - - -class CheckParity(unittest.TestCase): - - def test_parity_option2(self): - before_2k = unhexlify("CABF326FA56734324FFCCABCDEFACABF") - after_2k = DES3.adjust_key_parity(before_2k) - self.assertEqual(after_2k, - unhexlify("CBBF326EA46734324FFDCBBCDFFBCBBF")) - - def test_parity_option3(self): - before_3k = unhexlify("AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCC") - after_3k = DES3.adjust_key_parity(before_3k) - self.assertEqual(after_3k, - unhexlify("ABABABABABABABABBABABABABABABABACDCDCDCDCDCDCDCD")) - - def test_degradation(self): - sub_key1 = bchr(1) * 8 - sub_key2 = bchr(255) * 8 - - # K1 == K2 - self.assertRaises(ValueError, DES3.adjust_key_parity, - sub_key1 * 2 + sub_key2) - - # K2 == K3 - self.assertRaises(ValueError, DES3.adjust_key_parity, - sub_key1 + sub_key2 * 2) - - # K1 == K2 == K3 - self.assertRaises(ValueError, DES3.adjust_key_parity, - sub_key1 * 3) - - # K1 == K2 (with different parity) - self.assertRaises(ValueError, DES3.adjust_key_parity, - sub_key1 + strxor_c(sub_key1, 1) + sub_key2) - - -class DegenerateToDESTest(unittest.TestCase): - - def runTest(self): - sub_key1 = bchr(1) * 8 - sub_key2 = bchr(255) * 8 - - # K1 == K2 - self.assertRaises(ValueError, DES3.new, - sub_key1 * 2 + sub_key2, - DES3.MODE_ECB) - - # K2 == K3 - self.assertRaises(ValueError, DES3.new, - sub_key1 + sub_key2 * 2, - DES3.MODE_ECB) - - # K1 == K2 == K3 - self.assertRaises(ValueError, DES3.new, - sub_key1 * 3, - DES3.MODE_ECB) - - # K2 == K3 (parity is ignored) - self.assertRaises(ValueError, DES3.new, - sub_key1 + sub_key2 + strxor_c(sub_key2, 0x1), - DES3.MODE_ECB) - - -class TestOutput(unittest.TestCase): - - def runTest(self): - # Encrypt/Decrypt data and test output parameter - - cipher = DES3.new(b'4'*8 + b'G'*8 + b'T'*8, DES3.MODE_ECB) - - pt = b'5' * 16 - ct = cipher.encrypt(pt) - - output = bytearray(16) - res = cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - self.assertEqual(res, None) - - res = cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - self.assertEqual(res, None) - - output = memoryview(bytearray(16)) - cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - - cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - - self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0'*16) - self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0'*16) - - shorter_output = bytearray(7) - self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output) - self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output) - - -def get_tests(config={}): - from .common import make_block_tests - - tests = [] - tests = make_block_tests(DES3, "DES3", test_data) - tests.append(DegenerateToDESTest()) - tests += list_test_cases(CheckParity) - tests += [TestOutput()] - return tests - - -if __name__ == '__main__': - import unittest - - def suite(): - unittest.TestSuite(get_tests()) - - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_EAX.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_EAX.py deleted file mode 100644 index 4127a88..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_EAX.py +++ /dev/null @@ -1,773 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2015, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import unittest -from binascii import unhexlify - -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.SelfTest.loader import load_test_vectors_wycheproof -from Cryptodome.Util.py3compat import tobytes, bchr -from Cryptodome.Cipher import AES, DES3 -from Cryptodome.Hash import SHAKE128 - -from Cryptodome.Util.strxor import strxor - - -def get_tag_random(tag, length): - return SHAKE128.new(data=tobytes(tag)).read(length) - - -class EaxTests(unittest.TestCase): - - key_128 = get_tag_random("key_128", 16) - key_192 = get_tag_random("key_192", 16) - nonce_96 = get_tag_random("nonce_128", 12) - data_128 = get_tag_random("data_128", 16) - - def test_loopback_128(self): - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - pt = get_tag_random("plaintext", 16 * 100) - ct = cipher.encrypt(pt) - - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - pt2 = cipher.decrypt(ct) - self.assertEqual(pt, pt2) - - def test_loopback_64(self): - cipher = DES3.new(self.key_192, DES3.MODE_EAX, nonce=self.nonce_96) - pt = get_tag_random("plaintext", 8 * 100) - ct = cipher.encrypt(pt) - - cipher = DES3.new(self.key_192, DES3.MODE_EAX, nonce=self.nonce_96) - pt2 = cipher.decrypt(ct) - self.assertEqual(pt, pt2) - - def test_nonce(self): - # If not passed, the nonce is created randomly - cipher = AES.new(self.key_128, AES.MODE_EAX) - nonce1 = cipher.nonce - cipher = AES.new(self.key_128, AES.MODE_EAX) - nonce2 = cipher.nonce - self.assertEqual(len(nonce1), 16) - self.assertNotEqual(nonce1, nonce2) - - cipher = AES.new(self.key_128, AES.MODE_EAX, self.nonce_96) - ct = cipher.encrypt(self.data_128) - - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - self.assertEqual(ct, cipher.encrypt(self.data_128)) - - def test_nonce_must_be_bytes(self): - self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_EAX, - nonce=u'test12345678') - - def test_nonce_length(self): - # nonce can be of any length (but not empty) - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_EAX, - nonce=b"") - - for x in range(1, 128): - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=bchr(1) * x) - cipher.encrypt(bchr(1)) - - def test_block_size_128(self): - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - self.assertEqual(cipher.block_size, AES.block_size) - - def test_block_size_64(self): - cipher = DES3.new(self.key_192, AES.MODE_EAX, nonce=self.nonce_96) - self.assertEqual(cipher.block_size, DES3.block_size) - - def test_nonce_attribute(self): - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - self.assertEqual(cipher.nonce, self.nonce_96) - - # By default, a 16 bytes long nonce is randomly generated - nonce1 = AES.new(self.key_128, AES.MODE_EAX).nonce - nonce2 = AES.new(self.key_128, AES.MODE_EAX).nonce - self.assertEqual(len(nonce1), 16) - self.assertNotEqual(nonce1, nonce2) - - def test_unknown_parameters(self): - self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_EAX, - self.nonce_96, 7) - self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_EAX, - nonce=self.nonce_96, unknown=7) - - # But some are only known by the base cipher - # (e.g. use_aesni consumed by the AES module) - AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96, - use_aesni=False) - - def test_null_encryption_decryption(self): - for func in "encrypt", "decrypt": - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - result = getattr(cipher, func)(b"") - self.assertEqual(result, b"") - - def test_either_encrypt_or_decrypt(self): - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - cipher.encrypt(b"") - self.assertRaises(TypeError, cipher.decrypt, b"") - - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - cipher.decrypt(b"") - self.assertRaises(TypeError, cipher.encrypt, b"") - - def test_data_must_be_bytes(self): - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.encrypt, u'test1234567890-*') - - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.decrypt, u'test1234567890-*') - - def test_mac_len(self): - # Invalid MAC length - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_EAX, - nonce=self.nonce_96, mac_len=2-1) - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_EAX, - nonce=self.nonce_96, mac_len=16+1) - - # Valid MAC length - for mac_len in range(2, 16 + 1): - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96, - mac_len=mac_len) - _, mac = cipher.encrypt_and_digest(self.data_128) - self.assertEqual(len(mac), mac_len) - - # Default MAC length - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - _, mac = cipher.encrypt_and_digest(self.data_128) - self.assertEqual(len(mac), 16) - - def test_invalid_mac(self): - from Cryptodome.Util.strxor import strxor_c - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - ct, mac = cipher.encrypt_and_digest(self.data_128) - - invalid_mac = strxor_c(mac, 0x01) - - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - self.assertRaises(ValueError, cipher.decrypt_and_verify, ct, - invalid_mac) - - def test_hex_mac(self): - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - mac_hex = cipher.hexdigest() - self.assertEqual(cipher.digest(), unhexlify(mac_hex)) - - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - cipher.hexverify(mac_hex) - - def test_message_chunks(self): - # Validate that both associated data and plaintext/ciphertext - # can be broken up in chunks of arbitrary length - - auth_data = get_tag_random("authenticated data", 127) - plaintext = get_tag_random("plaintext", 127) - - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - cipher.update(auth_data) - ciphertext, ref_mac = cipher.encrypt_and_digest(plaintext) - - def break_up(data, chunk_length): - return [data[i:i+chunk_length] for i in range(0, len(data), - chunk_length)] - - # Encryption - for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128: - - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - - for chunk in break_up(auth_data, chunk_length): - cipher.update(chunk) - pt2 = b"" - for chunk in break_up(ciphertext, chunk_length): - pt2 += cipher.decrypt(chunk) - self.assertEqual(plaintext, pt2) - cipher.verify(ref_mac) - - # Decryption - for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128: - - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - - for chunk in break_up(auth_data, chunk_length): - cipher.update(chunk) - ct2 = b"" - for chunk in break_up(plaintext, chunk_length): - ct2 += cipher.encrypt(chunk) - self.assertEqual(ciphertext, ct2) - self.assertEqual(cipher.digest(), ref_mac) - - def test_bytearray(self): - - # Encrypt - key_ba = bytearray(self.key_128) - nonce_ba = bytearray(self.nonce_96) - header_ba = bytearray(self.data_128) - data_ba = bytearray(self.data_128) - - cipher1 = AES.new(self.key_128, - AES.MODE_EAX, - nonce=self.nonce_96) - cipher1.update(self.data_128) - ct = cipher1.encrypt(self.data_128) - tag = cipher1.digest() - - cipher2 = AES.new(key_ba, - AES.MODE_EAX, - nonce=nonce_ba) - key_ba[:3] = b'\xFF\xFF\xFF' - nonce_ba[:3] = b'\xFF\xFF\xFF' - cipher2.update(header_ba) - header_ba[:3] = b'\xFF\xFF\xFF' - ct_test = cipher2.encrypt(data_ba) - data_ba[:3] = b'\x99\x99\x99' - tag_test = cipher2.digest() - - self.assertEqual(ct, ct_test) - self.assertEqual(tag, tag_test) - self.assertEqual(cipher1.nonce, cipher2.nonce) - - # Decrypt - key_ba = bytearray(self.key_128) - nonce_ba = bytearray(self.nonce_96) - header_ba = bytearray(self.data_128) - ct_ba = bytearray(ct) - tag_ba = bytearray(tag) - del data_ba - - cipher3 = AES.new(key_ba, - AES.MODE_EAX, - nonce=nonce_ba) - key_ba[:3] = b'\xFF\xFF\xFF' - nonce_ba[:3] = b'\xFF\xFF\xFF' - cipher3.update(header_ba) - header_ba[:3] = b'\xFF\xFF\xFF' - pt_test = cipher3.decrypt(ct_ba) - ct_ba[:3] = b'\xFF\xFF\xFF' - cipher3.verify(tag_ba) - - self.assertEqual(pt_test, self.data_128) - - def test_memoryview(self): - - # Encrypt - key_mv = memoryview(bytearray(self.key_128)) - nonce_mv = memoryview(bytearray(self.nonce_96)) - header_mv = memoryview(bytearray(self.data_128)) - data_mv = memoryview(bytearray(self.data_128)) - - cipher1 = AES.new(self.key_128, - AES.MODE_EAX, - nonce=self.nonce_96) - cipher1.update(self.data_128) - ct = cipher1.encrypt(self.data_128) - tag = cipher1.digest() - - cipher2 = AES.new(key_mv, - AES.MODE_EAX, - nonce=nonce_mv) - key_mv[:3] = b'\xFF\xFF\xFF' - nonce_mv[:3] = b'\xFF\xFF\xFF' - cipher2.update(header_mv) - header_mv[:3] = b'\xFF\xFF\xFF' - ct_test = cipher2.encrypt(data_mv) - data_mv[:3] = b'\x99\x99\x99' - tag_test = cipher2.digest() - - self.assertEqual(ct, ct_test) - self.assertEqual(tag, tag_test) - self.assertEqual(cipher1.nonce, cipher2.nonce) - - # Decrypt - key_mv = memoryview(bytearray(self.key_128)) - nonce_mv = memoryview(bytearray(self.nonce_96)) - header_mv = memoryview(bytearray(self.data_128)) - ct_mv = memoryview(bytearray(ct)) - tag_mv = memoryview(bytearray(tag)) - del data_mv - - cipher3 = AES.new(key_mv, - AES.MODE_EAX, - nonce=nonce_mv) - key_mv[:3] = b'\xFF\xFF\xFF' - nonce_mv[:3] = b'\xFF\xFF\xFF' - cipher3.update(header_mv) - header_mv[:3] = b'\xFF\xFF\xFF' - pt_test = cipher3.decrypt(ct_mv) - ct_mv[:3] = b'\x99\x99\x99' - cipher3.verify(tag_mv) - - self.assertEqual(pt_test, self.data_128) - - def test_output_param(self): - - pt = b'5' * 128 - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - ct = cipher.encrypt(pt) - tag = cipher.digest() - - output = bytearray(128) - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - res = cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - self.assertEqual(res, None) - - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - res = cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - self.assertEqual(res, None) - - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - res, tag_out = cipher.encrypt_and_digest(pt, output=output) - self.assertEqual(ct, output) - self.assertEqual(res, None) - self.assertEqual(tag, tag_out) - - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - res = cipher.decrypt_and_verify(ct, tag, output=output) - self.assertEqual(pt, output) - self.assertEqual(res, None) - - def test_output_param_memoryview(self): - - pt = b'5' * 128 - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - ct = cipher.encrypt(pt) - - output = memoryview(bytearray(128)) - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - - def test_output_param_neg(self): - LEN_PT = 16 - - pt = b'5' * LEN_PT - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - ct = cipher.encrypt(pt) - - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0' * LEN_PT) - - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0' * LEN_PT) - - shorter_output = bytearray(LEN_PT - 1) - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output) - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output) - - -class EaxFSMTests(unittest.TestCase): - - key_128 = get_tag_random("key_128", 16) - nonce_96 = get_tag_random("nonce_128", 12) - data_128 = get_tag_random("data_128", 16) - - def test_valid_init_encrypt_decrypt_digest_verify(self): - # No authenticated data, fixed plaintext - # Verify path INIT->ENCRYPT->DIGEST - cipher = AES.new(self.key_128, AES.MODE_EAX, - nonce=self.nonce_96) - ct = cipher.encrypt(self.data_128) - mac = cipher.digest() - - # Verify path INIT->DECRYPT->VERIFY - cipher = AES.new(self.key_128, AES.MODE_EAX, - nonce=self.nonce_96) - cipher.decrypt(ct) - cipher.verify(mac) - - def test_valid_init_update_digest_verify(self): - # No plaintext, fixed authenticated data - # Verify path INIT->UPDATE->DIGEST - cipher = AES.new(self.key_128, AES.MODE_EAX, - nonce=self.nonce_96) - cipher.update(self.data_128) - mac = cipher.digest() - - # Verify path INIT->UPDATE->VERIFY - cipher = AES.new(self.key_128, AES.MODE_EAX, - nonce=self.nonce_96) - cipher.update(self.data_128) - cipher.verify(mac) - - def test_valid_full_path(self): - # Fixed authenticated data, fixed plaintext - # Verify path INIT->UPDATE->ENCRYPT->DIGEST - cipher = AES.new(self.key_128, AES.MODE_EAX, - nonce=self.nonce_96) - cipher.update(self.data_128) - ct = cipher.encrypt(self.data_128) - mac = cipher.digest() - - # Verify path INIT->UPDATE->DECRYPT->VERIFY - cipher = AES.new(self.key_128, AES.MODE_EAX, - nonce=self.nonce_96) - cipher.update(self.data_128) - cipher.decrypt(ct) - cipher.verify(mac) - - def test_valid_init_digest(self): - # Verify path INIT->DIGEST - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - cipher.digest() - - def test_valid_init_verify(self): - # Verify path INIT->VERIFY - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - mac = cipher.digest() - - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - cipher.verify(mac) - - def test_valid_multiple_encrypt_or_decrypt(self): - for method_name in "encrypt", "decrypt": - for auth_data in (None, b"333", self.data_128, - self.data_128 + b"3"): - if auth_data is None: - assoc_len = None - else: - assoc_len = len(auth_data) - cipher = AES.new(self.key_128, AES.MODE_EAX, - nonce=self.nonce_96) - if auth_data is not None: - cipher.update(auth_data) - method = getattr(cipher, method_name) - method(self.data_128) - method(self.data_128) - method(self.data_128) - method(self.data_128) - - def test_valid_multiple_digest_or_verify(self): - # Multiple calls to digest - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - cipher.update(self.data_128) - first_mac = cipher.digest() - for x in range(4): - self.assertEqual(first_mac, cipher.digest()) - - # Multiple calls to verify - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - cipher.update(self.data_128) - for x in range(5): - cipher.verify(first_mac) - - def test_valid_encrypt_and_digest_decrypt_and_verify(self): - # encrypt_and_digest - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - cipher.update(self.data_128) - ct, mac = cipher.encrypt_and_digest(self.data_128) - - # decrypt_and_verify - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - cipher.update(self.data_128) - pt = cipher.decrypt_and_verify(ct, mac) - self.assertEqual(self.data_128, pt) - - def test_invalid_mixing_encrypt_decrypt(self): - # Once per method, with or without assoc. data - for method1_name, method2_name in (("encrypt", "decrypt"), - ("decrypt", "encrypt")): - for assoc_data_present in (True, False): - cipher = AES.new(self.key_128, AES.MODE_EAX, - nonce=self.nonce_96) - if assoc_data_present: - cipher.update(self.data_128) - getattr(cipher, method1_name)(self.data_128) - self.assertRaises(TypeError, getattr(cipher, method2_name), - self.data_128) - - def test_invalid_encrypt_or_update_after_digest(self): - for method_name in "encrypt", "update": - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - cipher.encrypt(self.data_128) - cipher.digest() - self.assertRaises(TypeError, getattr(cipher, method_name), - self.data_128) - - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - cipher.encrypt_and_digest(self.data_128) - - def test_invalid_decrypt_or_update_after_verify(self): - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - ct = cipher.encrypt(self.data_128) - mac = cipher.digest() - - for method_name in "decrypt", "update": - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - cipher.decrypt(ct) - cipher.verify(mac) - self.assertRaises(TypeError, getattr(cipher, method_name), - self.data_128) - - cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) - cipher.decrypt_and_verify(ct, mac) - self.assertRaises(TypeError, getattr(cipher, method_name), - self.data_128) - - -class TestVectorsPaper(unittest.TestCase): - """Class exercising the EAX test vectors found in - http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf""" - - test_vectors_hex = [ - ( '6bfb914fd07eae6b', - '', - '', - 'e037830e8389f27b025a2d6527e79d01', - '233952dee4d5ed5f9b9c6d6ff80ff478', - '62EC67F9C3A4A407FCB2A8C49031A8B3' - ), - ( - 'fa3bfd4806eb53fa', - 'f7fb', - '19dd', - '5c4c9331049d0bdab0277408f67967e5', - '91945d3f4dcbee0bf45ef52255f095a4', - 'BECAF043B0A23D843194BA972C66DEBD' - ), - ( '234a3463c1264ac6', - '1a47cb4933', - 'd851d5bae0', - '3a59f238a23e39199dc9266626c40f80', - '01f74ad64077f2e704c0f60ada3dd523', - '70C3DB4F0D26368400A10ED05D2BFF5E' - ), - ( - '33cce2eabff5a79d', - '481c9e39b1', - '632a9d131a', - 'd4c168a4225d8e1ff755939974a7bede', - 'd07cf6cbb7f313bdde66b727afd3c5e8', - '8408DFFF3C1A2B1292DC199E46B7D617' - ), - ( - 'aeb96eaebe2970e9', - '40d0c07da5e4', - '071dfe16c675', - 'cb0677e536f73afe6a14b74ee49844dd', - '35b6d0580005bbc12b0587124557d2c2', - 'FDB6B06676EEDC5C61D74276E1F8E816' - ), - ( - 'd4482d1ca78dce0f', - '4de3b35c3fc039245bd1fb7d', - '835bb4f15d743e350e728414', - 'abb8644fd6ccb86947c5e10590210a4f', - 'bd8e6e11475e60b268784c38c62feb22', - '6EAC5C93072D8E8513F750935E46DA1B' - ), - ( - '65d2017990d62528', - '8b0a79306c9ce7ed99dae4f87f8dd61636', - '02083e3979da014812f59f11d52630da30', - '137327d10649b0aa6e1c181db617d7f2', - '7c77d6e813bed5ac98baa417477a2e7d', - '1A8C98DCD73D38393B2BF1569DEEFC19' - ), - ( - '54b9f04e6a09189a', - '1bda122bce8a8dbaf1877d962b8592dd2d56', - '2ec47b2c4954a489afc7ba4897edcdae8cc3', - '3b60450599bd02c96382902aef7f832a', - '5fff20cafab119ca2fc73549e20f5b0d', - 'DDE59B97D722156D4D9AFF2BC7559826' - ), - ( - '899a175897561d7e', - '6cf36720872b8513f6eab1a8a44438d5ef11', - '0de18fd0fdd91e7af19f1d8ee8733938b1e8', - 'e7f6d2231618102fdb7fe55ff1991700', - 'a4a4782bcffd3ec5e7ef6d8c34a56123', - 'B781FCF2F75FA5A8DE97A9CA48E522EC' - ), - ( - '126735fcc320d25a', - 'ca40d7446e545ffaed3bd12a740a659ffbbb3ceab7', - 'cb8920f87a6c75cff39627b56e3ed197c552d295a7', - 'cfc46afc253b4652b1af3795b124ab6e', - '8395fcf1e95bebd697bd010bc766aac3', - '22E7ADD93CFC6393C57EC0B3C17D6B44' - ), - ] - - test_vectors = [[unhexlify(x) for x in tv] for tv in test_vectors_hex] - - def runTest(self): - for assoc_data, pt, ct, mac, key, nonce in self.test_vectors: - # Encrypt - cipher = AES.new(key, AES.MODE_EAX, nonce, mac_len=len(mac)) - cipher.update(assoc_data) - ct2, mac2 = cipher.encrypt_and_digest(pt) - self.assertEqual(ct, ct2) - self.assertEqual(mac, mac2) - - # Decrypt - cipher = AES.new(key, AES.MODE_EAX, nonce, mac_len=len(mac)) - cipher.update(assoc_data) - pt2 = cipher.decrypt_and_verify(ct, mac) - self.assertEqual(pt, pt2) - - -class TestVectorsWycheproof(unittest.TestCase): - - def __init__(self, wycheproof_warnings): - unittest.TestCase.__init__(self) - self._wycheproof_warnings = wycheproof_warnings - self._id = "None" - - def setUp(self): - - def filter_tag(group): - return group['tagSize'] // 8 - - self.tv = load_test_vectors_wycheproof(("Cipher", "wycheproof"), - "aes_eax_test.json", - "Wycheproof EAX", - group_tag={'tag_size': filter_tag}) - - def shortDescription(self): - return self._id - - def warn(self, tv): - if tv.warning and self._wycheproof_warnings: - import warnings - warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment)) - - def test_encrypt(self, tv): - self._id = "Wycheproof Encrypt EAX Test #" + str(tv.id) - - try: - cipher = AES.new(tv.key, AES.MODE_EAX, tv.iv, mac_len=tv.tag_size) - except ValueError as e: - assert len(tv.iv) == 0 and "Nonce cannot be empty" in str(e) - return - - cipher.update(tv.aad) - ct, tag = cipher.encrypt_and_digest(tv.msg) - if tv.valid: - self.assertEqual(ct, tv.ct) - self.assertEqual(tag, tv.tag) - self.warn(tv) - - def test_decrypt(self, tv): - self._id = "Wycheproof Decrypt EAX Test #" + str(tv.id) - - try: - cipher = AES.new(tv.key, AES.MODE_EAX, tv.iv, mac_len=tv.tag_size) - except ValueError as e: - assert len(tv.iv) == 0 and "Nonce cannot be empty" in str(e) - return - - cipher.update(tv.aad) - try: - pt = cipher.decrypt_and_verify(tv.ct, tv.tag) - except ValueError: - assert not tv.valid - else: - assert tv.valid - self.assertEqual(pt, tv.msg) - self.warn(tv) - - def test_corrupt_decrypt(self, tv): - self._id = "Wycheproof Corrupt Decrypt EAX Test #" + str(tv.id) - if len(tv.iv) == 0 or len(tv.ct) < 1: - return - cipher = AES.new(tv.key, AES.MODE_EAX, tv.iv, mac_len=tv.tag_size) - cipher.update(tv.aad) - ct_corrupt = strxor(tv.ct, b"\x00" * (len(tv.ct) - 1) + b"\x01") - self.assertRaises(ValueError, cipher.decrypt_and_verify, ct_corrupt, tv.tag) - - def runTest(self): - - for tv in self.tv: - self.test_encrypt(tv) - self.test_decrypt(tv) - self.test_corrupt_decrypt(tv) - - -class TestOtherCiphers(unittest.TestCase): - - @classmethod - def create_test(cls, name, factory, key_size): - - def test_template(self, factory=factory, key_size=key_size): - cipher = factory.new(get_tag_random("cipher", key_size), - factory.MODE_EAX, - nonce=b"nonce") - ct, mac = cipher.encrypt_and_digest(b"plaintext") - - cipher = factory.new(get_tag_random("cipher", key_size), - factory.MODE_EAX, - nonce=b"nonce") - pt2 = cipher.decrypt_and_verify(ct, mac) - - self.assertEqual(b"plaintext", pt2) - - setattr(cls, "test_" + name, test_template) - - -from Cryptodome.Cipher import DES, DES3, ARC2, CAST, Blowfish - -TestOtherCiphers.create_test("DES_" + str(DES.key_size), DES, DES.key_size) -for ks in DES3.key_size: - TestOtherCiphers.create_test("DES3_" + str(ks), DES3, ks) -for ks in ARC2.key_size: - TestOtherCiphers.create_test("ARC2_" + str(ks), ARC2, ks) -for ks in CAST.key_size: - TestOtherCiphers.create_test("CAST_" + str(ks), CAST, ks) -for ks in Blowfish.key_size: - TestOtherCiphers.create_test("Blowfish_" + str(ks), Blowfish, ks) - - -def get_tests(config={}): - wycheproof_warnings = config.get('wycheproof_warnings') - - tests = [] - tests += list_test_cases(EaxTests) - tests += list_test_cases(EaxFSMTests) - tests += [ TestVectorsPaper() ] - tests += [ TestVectorsWycheproof(wycheproof_warnings) ] - tests += list_test_cases(TestOtherCiphers) - return tests - - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_GCM.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_GCM.py deleted file mode 100644 index ac8e741..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_GCM.py +++ /dev/null @@ -1,951 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2015, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from __future__ import print_function - -import unittest -from binascii import unhexlify - -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.SelfTest.loader import load_test_vectors, load_test_vectors_wycheproof - -from Cryptodome.Util.py3compat import tobytes, bchr -from Cryptodome.Cipher import AES -from Cryptodome.Hash import SHAKE128, SHA256 - -from Cryptodome.Util.strxor import strxor - - -def get_tag_random(tag, length): - return SHAKE128.new(data=tobytes(tag)).read(length) - - -class GcmTests(unittest.TestCase): - - key_128 = get_tag_random("key_128", 16) - nonce_96 = get_tag_random("nonce_128", 12) - data = get_tag_random("data", 128) - - def test_loopback_128(self): - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - pt = get_tag_random("plaintext", 16 * 100) - ct = cipher.encrypt(pt) - - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - pt2 = cipher.decrypt(ct) - self.assertEqual(pt, pt2) - - def test_nonce(self): - # Nonce is optional (a random one will be created) - AES.new(self.key_128, AES.MODE_GCM) - - cipher = AES.new(self.key_128, AES.MODE_GCM, self.nonce_96) - ct = cipher.encrypt(self.data) - - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - self.assertEqual(ct, cipher.encrypt(self.data)) - - def test_nonce_must_be_bytes(self): - self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_GCM, - nonce=u'test12345678') - - def test_nonce_length(self): - # nonce can be of any length (but not empty) - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_GCM, - nonce=b"") - - for x in range(1, 128): - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=bchr(1) * x) - cipher.encrypt(bchr(1)) - - def test_block_size_128(self): - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - self.assertEqual(cipher.block_size, AES.block_size) - - def test_nonce_attribute(self): - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - self.assertEqual(cipher.nonce, self.nonce_96) - - # By default, a 15 bytes long nonce is randomly generated - nonce1 = AES.new(self.key_128, AES.MODE_GCM).nonce - nonce2 = AES.new(self.key_128, AES.MODE_GCM).nonce - self.assertEqual(len(nonce1), 16) - self.assertNotEqual(nonce1, nonce2) - - def test_unknown_parameters(self): - self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_GCM, - self.nonce_96, 7) - self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_GCM, - nonce=self.nonce_96, unknown=7) - - # But some are only known by the base cipher - # (e.g. use_aesni consumed by the AES module) - AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96, - use_aesni=False) - - def test_null_encryption_decryption(self): - for func in "encrypt", "decrypt": - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - result = getattr(cipher, func)(b"") - self.assertEqual(result, b"") - - def test_either_encrypt_or_decrypt(self): - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - cipher.encrypt(b"") - self.assertRaises(TypeError, cipher.decrypt, b"") - - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - cipher.decrypt(b"") - self.assertRaises(TypeError, cipher.encrypt, b"") - - def test_data_must_be_bytes(self): - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.encrypt, u'test1234567890-*') - - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.decrypt, u'test1234567890-*') - - def test_mac_len(self): - # Invalid MAC length - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_GCM, - nonce=self.nonce_96, mac_len=3) - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_GCM, - nonce=self.nonce_96, mac_len=16+1) - - # Valid MAC length - for mac_len in range(5, 16 + 1): - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96, - mac_len=mac_len) - _, mac = cipher.encrypt_and_digest(self.data) - self.assertEqual(len(mac), mac_len) - - # Default MAC length - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - _, mac = cipher.encrypt_and_digest(self.data) - self.assertEqual(len(mac), 16) - - def test_invalid_mac(self): - from Cryptodome.Util.strxor import strxor_c - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - ct, mac = cipher.encrypt_and_digest(self.data) - - invalid_mac = strxor_c(mac, 0x01) - - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - self.assertRaises(ValueError, cipher.decrypt_and_verify, ct, - invalid_mac) - - def test_hex_mac(self): - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - mac_hex = cipher.hexdigest() - self.assertEqual(cipher.digest(), unhexlify(mac_hex)) - - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - cipher.hexverify(mac_hex) - - def test_message_chunks(self): - # Validate that both associated data and plaintext/ciphertext - # can be broken up in chunks of arbitrary length - - auth_data = get_tag_random("authenticated data", 127) - plaintext = get_tag_random("plaintext", 127) - - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - cipher.update(auth_data) - ciphertext, ref_mac = cipher.encrypt_and_digest(plaintext) - - def break_up(data, chunk_length): - return [data[i:i+chunk_length] for i in range(0, len(data), - chunk_length)] - - # Encryption - for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128: - - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - - for chunk in break_up(auth_data, chunk_length): - cipher.update(chunk) - pt2 = b"" - for chunk in break_up(ciphertext, chunk_length): - pt2 += cipher.decrypt(chunk) - self.assertEqual(plaintext, pt2) - cipher.verify(ref_mac) - - # Decryption - for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128: - - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - - for chunk in break_up(auth_data, chunk_length): - cipher.update(chunk) - ct2 = b"" - for chunk in break_up(plaintext, chunk_length): - ct2 += cipher.encrypt(chunk) - self.assertEqual(ciphertext, ct2) - self.assertEqual(cipher.digest(), ref_mac) - - def test_bytearray(self): - - # Encrypt - key_ba = bytearray(self.key_128) - nonce_ba = bytearray(self.nonce_96) - header_ba = bytearray(self.data) - data_ba = bytearray(self.data) - - cipher1 = AES.new(self.key_128, - AES.MODE_GCM, - nonce=self.nonce_96) - cipher1.update(self.data) - ct = cipher1.encrypt(self.data) - tag = cipher1.digest() - - cipher2 = AES.new(key_ba, - AES.MODE_GCM, - nonce=nonce_ba) - key_ba[:3] = b"\xFF\xFF\xFF" - nonce_ba[:3] = b"\xFF\xFF\xFF" - cipher2.update(header_ba) - header_ba[:3] = b"\xFF\xFF\xFF" - ct_test = cipher2.encrypt(data_ba) - data_ba[:3] = b"\xFF\xFF\xFF" - tag_test = cipher2.digest() - - self.assertEqual(ct, ct_test) - self.assertEqual(tag, tag_test) - self.assertEqual(cipher1.nonce, cipher2.nonce) - - # Decrypt - key_ba = bytearray(self.key_128) - nonce_ba = bytearray(self.nonce_96) - header_ba = bytearray(self.data) - del data_ba - - cipher4 = AES.new(key_ba, - AES.MODE_GCM, - nonce=nonce_ba) - key_ba[:3] = b"\xFF\xFF\xFF" - nonce_ba[:3] = b"\xFF\xFF\xFF" - cipher4.update(header_ba) - header_ba[:3] = b"\xFF\xFF\xFF" - pt_test = cipher4.decrypt_and_verify(bytearray(ct_test), bytearray(tag_test)) - - self.assertEqual(self.data, pt_test) - - def test_memoryview(self): - - # Encrypt - key_mv = memoryview(bytearray(self.key_128)) - nonce_mv = memoryview(bytearray(self.nonce_96)) - header_mv = memoryview(bytearray(self.data)) - data_mv = memoryview(bytearray(self.data)) - - cipher1 = AES.new(self.key_128, - AES.MODE_GCM, - nonce=self.nonce_96) - cipher1.update(self.data) - ct = cipher1.encrypt(self.data) - tag = cipher1.digest() - - cipher2 = AES.new(key_mv, - AES.MODE_GCM, - nonce=nonce_mv) - key_mv[:3] = b"\xFF\xFF\xFF" - nonce_mv[:3] = b"\xFF\xFF\xFF" - cipher2.update(header_mv) - header_mv[:3] = b"\xFF\xFF\xFF" - ct_test = cipher2.encrypt(data_mv) - data_mv[:3] = b"\xFF\xFF\xFF" - tag_test = cipher2.digest() - - self.assertEqual(ct, ct_test) - self.assertEqual(tag, tag_test) - self.assertEqual(cipher1.nonce, cipher2.nonce) - - # Decrypt - key_mv = memoryview(bytearray(self.key_128)) - nonce_mv = memoryview(bytearray(self.nonce_96)) - header_mv = memoryview(bytearray(self.data)) - del data_mv - - cipher4 = AES.new(key_mv, - AES.MODE_GCM, - nonce=nonce_mv) - key_mv[:3] = b"\xFF\xFF\xFF" - nonce_mv[:3] = b"\xFF\xFF\xFF" - cipher4.update(header_mv) - header_mv[:3] = b"\xFF\xFF\xFF" - pt_test = cipher4.decrypt_and_verify(memoryview(ct_test), memoryview(tag_test)) - - self.assertEqual(self.data, pt_test) - - def test_output_param(self): - - pt = b'5' * 128 - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - ct = cipher.encrypt(pt) - tag = cipher.digest() - - output = bytearray(128) - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - res = cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - self.assertEqual(res, None) - - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - res = cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - self.assertEqual(res, None) - - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - res, tag_out = cipher.encrypt_and_digest(pt, output=output) - self.assertEqual(ct, output) - self.assertEqual(res, None) - self.assertEqual(tag, tag_out) - - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - res = cipher.decrypt_and_verify(ct, tag, output=output) - self.assertEqual(pt, output) - self.assertEqual(res, None) - - def test_output_param_memoryview(self): - - pt = b'5' * 128 - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - ct = cipher.encrypt(pt) - - output = memoryview(bytearray(128)) - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - - def test_output_param_neg(self): - LEN_PT = 128 - - pt = b'5' * LEN_PT - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - ct = cipher.encrypt(pt) - - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0' * LEN_PT) - - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0' * LEN_PT) - - shorter_output = bytearray(LEN_PT - 1) - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output) - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output) - - -class GcmFSMTests(unittest.TestCase): - - key_128 = get_tag_random("key_128", 16) - nonce_96 = get_tag_random("nonce_128", 12) - data = get_tag_random("data", 128) - - def test_valid_init_encrypt_decrypt_digest_verify(self): - # No authenticated data, fixed plaintext - # Verify path INIT->ENCRYPT->DIGEST - cipher = AES.new(self.key_128, AES.MODE_GCM, - nonce=self.nonce_96) - ct = cipher.encrypt(self.data) - mac = cipher.digest() - - # Verify path INIT->DECRYPT->VERIFY - cipher = AES.new(self.key_128, AES.MODE_GCM, - nonce=self.nonce_96) - cipher.decrypt(ct) - cipher.verify(mac) - - def test_valid_init_update_digest_verify(self): - # No plaintext, fixed authenticated data - # Verify path INIT->UPDATE->DIGEST - cipher = AES.new(self.key_128, AES.MODE_GCM, - nonce=self.nonce_96) - cipher.update(self.data) - mac = cipher.digest() - - # Verify path INIT->UPDATE->VERIFY - cipher = AES.new(self.key_128, AES.MODE_GCM, - nonce=self.nonce_96) - cipher.update(self.data) - cipher.verify(mac) - - def test_valid_full_path(self): - # Fixed authenticated data, fixed plaintext - # Verify path INIT->UPDATE->ENCRYPT->DIGEST - cipher = AES.new(self.key_128, AES.MODE_GCM, - nonce=self.nonce_96) - cipher.update(self.data) - ct = cipher.encrypt(self.data) - mac = cipher.digest() - - # Verify path INIT->UPDATE->DECRYPT->VERIFY - cipher = AES.new(self.key_128, AES.MODE_GCM, - nonce=self.nonce_96) - cipher.update(self.data) - cipher.decrypt(ct) - cipher.verify(mac) - - def test_valid_init_digest(self): - # Verify path INIT->DIGEST - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - cipher.digest() - - def test_valid_init_verify(self): - # Verify path INIT->VERIFY - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - mac = cipher.digest() - - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - cipher.verify(mac) - - def test_valid_multiple_encrypt_or_decrypt(self): - for method_name in "encrypt", "decrypt": - for auth_data in (None, b"333", self.data, - self.data + b"3"): - if auth_data is None: - assoc_len = None - else: - assoc_len = len(auth_data) - cipher = AES.new(self.key_128, AES.MODE_GCM, - nonce=self.nonce_96) - if auth_data is not None: - cipher.update(auth_data) - method = getattr(cipher, method_name) - method(self.data) - method(self.data) - method(self.data) - method(self.data) - - def test_valid_multiple_digest_or_verify(self): - # Multiple calls to digest - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - cipher.update(self.data) - first_mac = cipher.digest() - for x in range(4): - self.assertEqual(first_mac, cipher.digest()) - - # Multiple calls to verify - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - cipher.update(self.data) - for x in range(5): - cipher.verify(first_mac) - - def test_valid_encrypt_and_digest_decrypt_and_verify(self): - # encrypt_and_digest - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - cipher.update(self.data) - ct, mac = cipher.encrypt_and_digest(self.data) - - # decrypt_and_verify - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - cipher.update(self.data) - pt = cipher.decrypt_and_verify(ct, mac) - self.assertEqual(self.data, pt) - - def test_invalid_mixing_encrypt_decrypt(self): - # Once per method, with or without assoc. data - for method1_name, method2_name in (("encrypt", "decrypt"), - ("decrypt", "encrypt")): - for assoc_data_present in (True, False): - cipher = AES.new(self.key_128, AES.MODE_GCM, - nonce=self.nonce_96) - if assoc_data_present: - cipher.update(self.data) - getattr(cipher, method1_name)(self.data) - self.assertRaises(TypeError, getattr(cipher, method2_name), - self.data) - - def test_invalid_encrypt_or_update_after_digest(self): - for method_name in "encrypt", "update": - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - cipher.encrypt(self.data) - cipher.digest() - self.assertRaises(TypeError, getattr(cipher, method_name), - self.data) - - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - cipher.encrypt_and_digest(self.data) - - def test_invalid_decrypt_or_update_after_verify(self): - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - ct = cipher.encrypt(self.data) - mac = cipher.digest() - - for method_name in "decrypt", "update": - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - cipher.decrypt(ct) - cipher.verify(mac) - self.assertRaises(TypeError, getattr(cipher, method_name), - self.data) - - cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96) - cipher.decrypt_and_verify(ct, mac) - self.assertRaises(TypeError, getattr(cipher, method_name), - self.data) - - -class TestVectors(unittest.TestCase): - """Class exercising the GCM test vectors found in - http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf""" - - # List of test vectors, each made up of: - # - authenticated data - # - plaintext - # - ciphertext - # - MAC - # - AES key - # - nonce - test_vectors_hex = [ - ( - '', - '', - '', - '58e2fccefa7e3061367f1d57a4e7455a', - '00000000000000000000000000000000', - '000000000000000000000000' - ), - ( - '', - '00000000000000000000000000000000', - '0388dace60b6a392f328c2b971b2fe78', - 'ab6e47d42cec13bdf53a67b21257bddf', - '00000000000000000000000000000000', - '000000000000000000000000' - ), - ( - '', - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255', - '42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e' + - '21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985', - '4d5c2af327cd64a62cf35abd2ba6fab4', - 'feffe9928665731c6d6a8f9467308308', - 'cafebabefacedbaddecaf888' - ), - ( - 'feedfacedeadbeeffeedfacedeadbeefabaddad2', - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - '42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e' + - '21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091', - '5bc94fbc3221a5db94fae95ae7121a47', - 'feffe9928665731c6d6a8f9467308308', - 'cafebabefacedbaddecaf888' - ), - ( - 'feedfacedeadbeeffeedfacedeadbeefabaddad2', - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - '61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c7423' + - '73806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f4598', - '3612d2e79e3b0785561be14aaca2fccb', - 'feffe9928665731c6d6a8f9467308308', - 'cafebabefacedbad' - ), - ( - 'feedfacedeadbeeffeedfacedeadbeefabaddad2', - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - '8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca7' + - '01e4a9a4fba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5', - '619cc5aefffe0bfa462af43c1699d050', - 'feffe9928665731c6d6a8f9467308308', - '9313225df88406e555909c5aff5269aa' + - '6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b5254' + - '16aedbf5a0de6a57a637b39b' - ), - ( - '', - '', - '', - 'cd33b28ac773f74ba00ed1f312572435', - '000000000000000000000000000000000000000000000000', - '000000000000000000000000' - ), - ( - '', - '00000000000000000000000000000000', - '98e7247c07f0fe411c267e4384b0f600', - '2ff58d80033927ab8ef4d4587514f0fb', - '000000000000000000000000000000000000000000000000', - '000000000000000000000000' - ), - ( - '', - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255', - '3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c' + - '7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710acade256', - '9924a7c8587336bfb118024db8674a14', - 'feffe9928665731c6d6a8f9467308308feffe9928665731c', - 'cafebabefacedbaddecaf888' - ), - ( - 'feedfacedeadbeeffeedfacedeadbeefabaddad2', - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - '3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c' + - '7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710', - '2519498e80f1478f37ba55bd6d27618c', - 'feffe9928665731c6d6a8f9467308308feffe9928665731c', - 'cafebabefacedbaddecaf888' - ), - ( - 'feedfacedeadbeeffeedfacedeadbeefabaddad2', - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - '0f10f599ae14a154ed24b36e25324db8c566632ef2bbb34f8347280fc4507057' + - 'fddc29df9a471f75c66541d4d4dad1c9e93a19a58e8b473fa0f062f7', - '65dcc57fcf623a24094fcca40d3533f8', - 'feffe9928665731c6d6a8f9467308308feffe9928665731c', - 'cafebabefacedbad' - ), - ( - 'feedfacedeadbeeffeedfacedeadbeefabaddad2', - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'd27e88681ce3243c4830165a8fdcf9ff1de9a1d8e6b447ef6ef7b79828666e45' + - '81e79012af34ddd9e2f037589b292db3e67c036745fa22e7e9b7373b', - 'dcf566ff291c25bbb8568fc3d376a6d9', - 'feffe9928665731c6d6a8f9467308308feffe9928665731c', - '9313225df88406e555909c5aff5269aa' + - '6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b5254' + - '16aedbf5a0de6a57a637b39b' - ), - ( - '', - '', - '', - '530f8afbc74536b9a963b4f1c4cb738b', - '0000000000000000000000000000000000000000000000000000000000000000', - '000000000000000000000000' - ), - ( - '', - '00000000000000000000000000000000', - 'cea7403d4d606b6e074ec5d3baf39d18', - 'd0d1c8a799996bf0265b98b5d48ab919', - '0000000000000000000000000000000000000000000000000000000000000000', - '000000000000000000000000' - ), - ( '', - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255', - '522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa' + - '8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad', - 'b094dac5d93471bdec1a502270e3cc6c', - 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', - 'cafebabefacedbaddecaf888' - ), - ( - 'feedfacedeadbeeffeedfacedeadbeefabaddad2', - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - '522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa' + - '8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662', - '76fc6ece0f4e1768cddf8853bb2d551b', - 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', - 'cafebabefacedbaddecaf888' - ), - ( - 'feedfacedeadbeeffeedfacedeadbeefabaddad2', - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'c3762df1ca787d32ae47c13bf19844cbaf1ae14d0b976afac52ff7d79bba9de0' + - 'feb582d33934a4f0954cc2363bc73f7862ac430e64abe499f47c9b1f', - '3a337dbf46a792c45e454913fe2ea8f2', - 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', - 'cafebabefacedbad' - ), - ( - 'feedfacedeadbeeffeedfacedeadbeefabaddad2', - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - '5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf4' + - '0fc0c3b780f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3f', - 'a44a8266ee1c8eb0c8b5d4cf5ae9f19a', - 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', - '9313225df88406e555909c5aff5269aa' + - '6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b5254' + - '16aedbf5a0de6a57a637b39b' - ) - ] - - test_vectors = [[unhexlify(x) for x in tv] for tv in test_vectors_hex] - - def runTest(self): - for assoc_data, pt, ct, mac, key, nonce in self.test_vectors: - - # Encrypt - cipher = AES.new(key, AES.MODE_GCM, nonce, mac_len=len(mac)) - cipher.update(assoc_data) - ct2, mac2 = cipher.encrypt_and_digest(pt) - self.assertEqual(ct, ct2) - self.assertEqual(mac, mac2) - - # Decrypt - cipher = AES.new(key, AES.MODE_GCM, nonce, mac_len=len(mac)) - cipher.update(assoc_data) - pt2 = cipher.decrypt_and_verify(ct, mac) - self.assertEqual(pt, pt2) - - -class TestVectorsGueronKrasnov(unittest.TestCase): - """Class exercising the GCM test vectors found in - 'The fragility of AES-GCM authentication algorithm', Gueron, Krasnov - https://eprint.iacr.org/2013/157.pdf""" - - def test_1(self): - key = unhexlify("3da6c536d6295579c0959a7043efb503") - iv = unhexlify("2b926197d34e091ef722db94") - aad = unhexlify("00000000000000000000000000000000" + - "000102030405060708090a0b0c0d0e0f" + - "101112131415161718191a1b1c1d1e1f" + - "202122232425262728292a2b2c2d2e2f" + - "303132333435363738393a3b3c3d3e3f") - digest = unhexlify("69dd586555ce3fcc89663801a71d957b") - - cipher = AES.new(key, AES.MODE_GCM, iv).update(aad) - self.assertEqual(digest, cipher.digest()) - - def test_2(self): - key = unhexlify("843ffcf5d2b72694d19ed01d01249412") - iv = unhexlify("dbcca32ebf9b804617c3aa9e") - aad = unhexlify("00000000000000000000000000000000" + - "101112131415161718191a1b1c1d1e1f") - pt = unhexlify("000102030405060708090a0b0c0d0e0f" + - "101112131415161718191a1b1c1d1e1f" + - "202122232425262728292a2b2c2d2e2f" + - "303132333435363738393a3b3c3d3e3f" + - "404142434445464748494a4b4c4d4e4f") - ct = unhexlify("6268c6fa2a80b2d137467f092f657ac0" + - "4d89be2beaa623d61b5a868c8f03ff95" + - "d3dcee23ad2f1ab3a6c80eaf4b140eb0" + - "5de3457f0fbc111a6b43d0763aa422a3" + - "013cf1dc37fe417d1fbfc449b75d4cc5") - digest = unhexlify("3b629ccfbc1119b7319e1dce2cd6fd6d") - - cipher = AES.new(key, AES.MODE_GCM, iv).update(aad) - ct2, digest2 = cipher.encrypt_and_digest(pt) - - self.assertEqual(ct, ct2) - self.assertEqual(digest, digest2) - - -class NISTTestVectorsGCM(unittest.TestCase): - - def __init__(self, a): - self.use_clmul = True - unittest.TestCase.__init__(self, a) - - -class NISTTestVectorsGCM_no_clmul(unittest.TestCase): - - def __init__(self, a): - self.use_clmul = False - unittest.TestCase.__init__(self, a) - - -test_vectors_nist = load_test_vectors( - ("Cipher", "AES"), - "gcmDecrypt128.rsp", - "GCM decrypt", - {"count": lambda x: int(x)}) or [] - -test_vectors_nist += load_test_vectors( - ("Cipher", "AES"), - "gcmEncryptExtIV128.rsp", - "GCM encrypt", - {"count": lambda x: int(x)}) or [] - -for idx, tv in enumerate(test_vectors_nist): - - # The test vector file contains some directive lines - if isinstance(tv, str): - continue - - def single_test(self, tv=tv): - - self.description = tv.desc - cipher = AES.new(tv.key, AES.MODE_GCM, nonce=tv.iv, - mac_len=len(tv.tag), use_clmul=self.use_clmul) - cipher.update(tv.aad) - if "FAIL" in tv.others: - self.assertRaises(ValueError, cipher.decrypt_and_verify, - tv.ct, tv.tag) - else: - pt = cipher.decrypt_and_verify(tv.ct, tv.tag) - self.assertEqual(pt, tv.pt) - - setattr(NISTTestVectorsGCM, "test_%d" % idx, single_test) - setattr(NISTTestVectorsGCM_no_clmul, "test_%d" % idx, single_test) - - -class TestVectorsWycheproof(unittest.TestCase): - - def __init__(self, wycheproof_warnings, **extra_params): - unittest.TestCase.__init__(self) - self._wycheproof_warnings = wycheproof_warnings - self._extra_params = extra_params - self._id = "None" - - def setUp(self): - - def filter_tag(group): - return group['tagSize'] // 8 - - self.tv = load_test_vectors_wycheproof(("Cipher", "wycheproof"), - "aes_gcm_test.json", - "Wycheproof GCM", - group_tag={'tag_size': filter_tag}) - - def shortDescription(self): - return self._id - - def warn(self, tv): - if tv.warning and self._wycheproof_warnings: - import warnings - warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment)) - - def test_encrypt(self, tv): - self._id = "Wycheproof Encrypt GCM Test #" + str(tv.id) - - try: - cipher = AES.new(tv.key, AES.MODE_GCM, tv.iv, mac_len=tv.tag_size, - **self._extra_params) - except ValueError as e: - if len(tv.iv) == 0 and "Nonce cannot be empty" in str(e): - return - raise e - - cipher.update(tv.aad) - ct, tag = cipher.encrypt_and_digest(tv.msg) - if tv.valid: - self.assertEqual(ct, tv.ct) - self.assertEqual(tag, tv.tag) - self.warn(tv) - - def test_decrypt(self, tv): - self._id = "Wycheproof Decrypt GCM Test #" + str(tv.id) - - try: - cipher = AES.new(tv.key, AES.MODE_GCM, tv.iv, mac_len=tv.tag_size, - **self._extra_params) - except ValueError as e: - if len(tv.iv) == 0 and "Nonce cannot be empty" in str(e): - return - raise e - - cipher.update(tv.aad) - try: - pt = cipher.decrypt_and_verify(tv.ct, tv.tag) - except ValueError: - assert not tv.valid - else: - assert tv.valid - self.assertEqual(pt, tv.msg) - self.warn(tv) - - def test_corrupt_decrypt(self, tv): - self._id = "Wycheproof Corrupt Decrypt GCM Test #" + str(tv.id) - if len(tv.iv) == 0 or len(tv.ct) < 1: - return - cipher = AES.new(tv.key, AES.MODE_GCM, tv.iv, mac_len=tv.tag_size, - **self._extra_params) - cipher.update(tv.aad) - ct_corrupt = strxor(tv.ct, b"\x00" * (len(tv.ct) - 1) + b"\x01") - self.assertRaises(ValueError, cipher.decrypt_and_verify, ct_corrupt, tv.tag) - - def runTest(self): - - for tv in self.tv: - self.test_encrypt(tv) - self.test_decrypt(tv) - self.test_corrupt_decrypt(tv) - - -class TestVariableLength(unittest.TestCase): - - def __init__(self, **extra_params): - unittest.TestCase.__init__(self) - self._extra_params = extra_params - - def runTest(self): - key = b'0' * 16 - h = SHA256.new() - - for length in range(160): - nonce = '{0:04d}'.format(length).encode('utf-8') - data = bchr(length) * length - cipher = AES.new(key, AES.MODE_GCM, nonce=nonce, **self._extra_params) - ct, tag = cipher.encrypt_and_digest(data) - h.update(ct) - h.update(tag) - - self.assertEqual(h.hexdigest(), "7b7eb1ffbe67a2e53a912067c0ec8e62ebc7ce4d83490ea7426941349811bdf4") - - -def get_tests(config={}): - from Cryptodome.Util import _cpu_features - - wycheproof_warnings = config.get('wycheproof_warnings') - - tests = [] - tests += list_test_cases(GcmTests) - tests += list_test_cases(GcmFSMTests) - tests += [TestVectors()] - tests += [TestVectorsWycheproof(wycheproof_warnings)] - tests += list_test_cases(TestVectorsGueronKrasnov) - tests += [TestVariableLength()] - if config.get('slow_tests'): - tests += list_test_cases(NISTTestVectorsGCM) - - if _cpu_features.have_clmul(): - tests += [TestVectorsWycheproof(wycheproof_warnings, use_clmul=False)] - tests += [TestVariableLength(use_clmul=False)] - if config.get('slow_tests'): - tests += list_test_cases(NISTTestVectorsGCM_no_clmul) - else: - print("Skipping test of PCLMULDQD in AES GCM") - - return tests - - -if __name__ == '__main__': - def suite(): - unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_OCB.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_OCB.py deleted file mode 100644 index 1f2ffbc..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_OCB.py +++ /dev/null @@ -1,845 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import unittest -from binascii import unhexlify - -from Cryptodome.Util.py3compat import b, tobytes, bchr -from Cryptodome.Util.number import long_to_bytes -from Cryptodome.SelfTest.loader import load_test_vectors -from Cryptodome.SelfTest.st_common import list_test_cases - -from Cryptodome.Cipher import AES -from Cryptodome.Hash import SHAKE128 - - -def get_tag_random(tag, length): - return SHAKE128.new(data=tobytes(tag)).read(length) - - -class OcbTests(unittest.TestCase): - - key_128 = get_tag_random("key_128", 16) - nonce_96 = get_tag_random("nonce_128", 12) - data = get_tag_random("data", 128) - - def test_loopback_128(self): - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - pt = get_tag_random("plaintext", 16 * 100) - ct, mac = cipher.encrypt_and_digest(pt) - - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - pt2 = cipher.decrypt_and_verify(ct, mac) - self.assertEqual(pt, pt2) - - def test_nonce(self): - # Nonce is optional - AES.new(self.key_128, AES.MODE_OCB) - - cipher = AES.new(self.key_128, AES.MODE_OCB, self.nonce_96) - ct = cipher.encrypt(self.data) - - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - self.assertEqual(ct, cipher.encrypt(self.data)) - - def test_nonce_must_be_bytes(self): - self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_OCB, - nonce=u'test12345678') - - def test_nonce_length(self): - # nonce cannot be empty - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_OCB, - nonce=b("")) - - # nonce can be up to 15 bytes long - for length in range(1, 16): - AES.new(self.key_128, AES.MODE_OCB, nonce=self.data[:length]) - - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_OCB, - nonce=self.data) - - def test_block_size_128(self): - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - self.assertEqual(cipher.block_size, AES.block_size) - - # By default, a 15 bytes long nonce is randomly generated - nonce1 = AES.new(self.key_128, AES.MODE_OCB).nonce - nonce2 = AES.new(self.key_128, AES.MODE_OCB).nonce - self.assertEqual(len(nonce1), 15) - self.assertNotEqual(nonce1, nonce2) - - def test_nonce_attribute(self): - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - self.assertEqual(cipher.nonce, self.nonce_96) - - # By default, a 15 bytes long nonce is randomly generated - nonce1 = AES.new(self.key_128, AES.MODE_OCB).nonce - nonce2 = AES.new(self.key_128, AES.MODE_OCB).nonce - self.assertEqual(len(nonce1), 15) - self.assertNotEqual(nonce1, nonce2) - - def test_unknown_parameters(self): - self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_OCB, - self.nonce_96, 7) - self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_OCB, - nonce=self.nonce_96, unknown=7) - - # But some are only known by the base cipher - # (e.g. use_aesni consumed by the AES module) - AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96, - use_aesni=False) - - def test_null_encryption_decryption(self): - for func in "encrypt", "decrypt": - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - result = getattr(cipher, func)(b("")) - self.assertEqual(result, b("")) - - def test_either_encrypt_or_decrypt(self): - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - cipher.encrypt(b("xyz")) - self.assertRaises(TypeError, cipher.decrypt, b("xyz")) - - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - cipher.decrypt(b("xyz")) - self.assertRaises(TypeError, cipher.encrypt, b("xyz")) - - def test_data_must_be_bytes(self): - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.encrypt, u'test1234567890-*') - - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.decrypt, u'test1234567890-*') - - def test_mac_len(self): - # Invalid MAC length - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_OCB, - nonce=self.nonce_96, mac_len=7) - self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_OCB, - nonce=self.nonce_96, mac_len=16+1) - - # Valid MAC length - for mac_len in range(8, 16 + 1): - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96, - mac_len=mac_len) - _, mac = cipher.encrypt_and_digest(self.data) - self.assertEqual(len(mac), mac_len) - - # Default MAC length - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - _, mac = cipher.encrypt_and_digest(self.data) - self.assertEqual(len(mac), 16) - - def test_invalid_mac(self): - from Cryptodome.Util.strxor import strxor_c - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - ct, mac = cipher.encrypt_and_digest(self.data) - - invalid_mac = strxor_c(mac, 0x01) - - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - self.assertRaises(ValueError, cipher.decrypt_and_verify, ct, - invalid_mac) - - def test_hex_mac(self): - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - mac_hex = cipher.hexdigest() - self.assertEqual(cipher.digest(), unhexlify(mac_hex)) - - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - cipher.hexverify(mac_hex) - - def test_message_chunks(self): - # Validate that both associated data and plaintext/ciphertext - # can be broken up in chunks of arbitrary length - - auth_data = get_tag_random("authenticated data", 127) - plaintext = get_tag_random("plaintext", 127) - - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - cipher.update(auth_data) - ciphertext, ref_mac = cipher.encrypt_and_digest(plaintext) - - def break_up(data, chunk_length): - return [data[i:i+chunk_length] for i in range(0, len(data), - chunk_length)] - - # Encryption - for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128: - - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - - for chunk in break_up(auth_data, chunk_length): - cipher.update(chunk) - pt2 = b("") - for chunk in break_up(ciphertext, chunk_length): - pt2 += cipher.decrypt(chunk) - pt2 += cipher.decrypt() - self.assertEqual(plaintext, pt2) - cipher.verify(ref_mac) - - # Decryption - for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128: - - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - - for chunk in break_up(auth_data, chunk_length): - cipher.update(chunk) - ct2 = b("") - for chunk in break_up(plaintext, chunk_length): - ct2 += cipher.encrypt(chunk) - ct2 += cipher.encrypt() - self.assertEqual(ciphertext, ct2) - self.assertEqual(cipher.digest(), ref_mac) - - def test_bytearray(self): - - # Encrypt - key_ba = bytearray(self.key_128) - nonce_ba = bytearray(self.nonce_96) - header_ba = bytearray(self.data) - data_ba = bytearray(self.data) - - cipher1 = AES.new(self.key_128, - AES.MODE_OCB, - nonce=self.nonce_96) - cipher1.update(self.data) - ct = cipher1.encrypt(self.data) + cipher1.encrypt() - tag = cipher1.digest() - - cipher2 = AES.new(key_ba, - AES.MODE_OCB, - nonce=nonce_ba) - key_ba[:3] = b"\xFF\xFF\xFF" - nonce_ba[:3] = b"\xFF\xFF\xFF" - cipher2.update(header_ba) - header_ba[:3] = b"\xFF\xFF\xFF" - ct_test = cipher2.encrypt(data_ba) + cipher2.encrypt() - data_ba[:3] = b"\xFF\xFF\xFF" - tag_test = cipher2.digest() - - self.assertEqual(ct, ct_test) - self.assertEqual(tag, tag_test) - self.assertEqual(cipher1.nonce, cipher2.nonce) - - # Decrypt - key_ba = bytearray(self.key_128) - nonce_ba = bytearray(self.nonce_96) - header_ba = bytearray(self.data) - del data_ba - - cipher4 = AES.new(key_ba, - AES.MODE_OCB, - nonce=nonce_ba) - key_ba[:3] = b"\xFF\xFF\xFF" - nonce_ba[:3] = b"\xFF\xFF\xFF" - cipher4.update(header_ba) - header_ba[:3] = b"\xFF\xFF\xFF" - pt_test = cipher4.decrypt_and_verify(bytearray(ct_test), bytearray(tag_test)) - - self.assertEqual(self.data, pt_test) - - def test_memoryview(self): - - # Encrypt - key_mv = memoryview(bytearray(self.key_128)) - nonce_mv = memoryview(bytearray(self.nonce_96)) - header_mv = memoryview(bytearray(self.data)) - data_mv = memoryview(bytearray(self.data)) - - cipher1 = AES.new(self.key_128, - AES.MODE_OCB, - nonce=self.nonce_96) - cipher1.update(self.data) - ct = cipher1.encrypt(self.data) + cipher1.encrypt() - tag = cipher1.digest() - - cipher2 = AES.new(key_mv, - AES.MODE_OCB, - nonce=nonce_mv) - key_mv[:3] = b"\xFF\xFF\xFF" - nonce_mv[:3] = b"\xFF\xFF\xFF" - cipher2.update(header_mv) - header_mv[:3] = b"\xFF\xFF\xFF" - ct_test = cipher2.encrypt(data_mv) + cipher2.encrypt() - data_mv[:3] = b"\xFF\xFF\xFF" - tag_test = cipher2.digest() - - self.assertEqual(ct, ct_test) - self.assertEqual(tag, tag_test) - self.assertEqual(cipher1.nonce, cipher2.nonce) - - # Decrypt - key_mv = memoryview(bytearray(self.key_128)) - nonce_mv = memoryview(bytearray(self.nonce_96)) - header_mv = memoryview(bytearray(self.data)) - del data_mv - - cipher4 = AES.new(key_mv, - AES.MODE_OCB, - nonce=nonce_mv) - key_mv[:3] = b"\xFF\xFF\xFF" - nonce_mv[:3] = b"\xFF\xFF\xFF" - cipher4.update(header_mv) - header_mv[:3] = b"\xFF\xFF\xFF" - pt_test = cipher4.decrypt_and_verify(memoryview(ct_test), memoryview(tag_test)) - - self.assertEqual(self.data, pt_test) - - -class OcbFSMTests(unittest.TestCase): - - key_128 = get_tag_random("key_128", 16) - nonce_96 = get_tag_random("nonce_128", 12) - data = get_tag_random("data", 128) - - def test_valid_init_encrypt_decrypt_digest_verify(self): - # No authenticated data, fixed plaintext - # Verify path INIT->ENCRYPT->ENCRYPT(NONE)->DIGEST - cipher = AES.new(self.key_128, AES.MODE_OCB, - nonce=self.nonce_96) - ct = cipher.encrypt(self.data) - ct += cipher.encrypt() - mac = cipher.digest() - - # Verify path INIT->DECRYPT->DECRYPT(NONCE)->VERIFY - cipher = AES.new(self.key_128, AES.MODE_OCB, - nonce=self.nonce_96) - cipher.decrypt(ct) - cipher.decrypt() - cipher.verify(mac) - - def test_invalid_init_encrypt_decrypt_digest_verify(self): - # No authenticated data, fixed plaintext - # Verify path INIT->ENCRYPT->DIGEST - cipher = AES.new(self.key_128, AES.MODE_OCB, - nonce=self.nonce_96) - ct = cipher.encrypt(self.data) - self.assertRaises(TypeError, cipher.digest) - - # Verify path INIT->DECRYPT->VERIFY - cipher = AES.new(self.key_128, AES.MODE_OCB, - nonce=self.nonce_96) - cipher.decrypt(ct) - self.assertRaises(TypeError, cipher.verify) - - def test_valid_init_update_digest_verify(self): - # No plaintext, fixed authenticated data - # Verify path INIT->UPDATE->DIGEST - cipher = AES.new(self.key_128, AES.MODE_OCB, - nonce=self.nonce_96) - cipher.update(self.data) - mac = cipher.digest() - - # Verify path INIT->UPDATE->VERIFY - cipher = AES.new(self.key_128, AES.MODE_OCB, - nonce=self.nonce_96) - cipher.update(self.data) - cipher.verify(mac) - - def test_valid_full_path(self): - # Fixed authenticated data, fixed plaintext - # Verify path INIT->UPDATE->ENCRYPT->ENCRYPT(NONE)->DIGEST - cipher = AES.new(self.key_128, AES.MODE_OCB, - nonce=self.nonce_96) - cipher.update(self.data) - ct = cipher.encrypt(self.data) - ct += cipher.encrypt() - mac = cipher.digest() - - # Verify path INIT->UPDATE->DECRYPT->DECRYPT(NONE)->VERIFY - cipher = AES.new(self.key_128, AES.MODE_OCB, - nonce=self.nonce_96) - cipher.update(self.data) - cipher.decrypt(ct) - cipher.decrypt() - cipher.verify(mac) - - # Verify path INIT->UPDATE->ENCRYPT->ENCRYPT_AND_DIGEST - cipher = AES.new(self.key_128, AES.MODE_OCB, - nonce=self.nonce_96) - cipher.update(self.data) - ct1 = cipher.encrypt(self.data[:2]) - ct2, mac = cipher.encrypt_and_digest(self.data[2:]) - - # Verify path INIT->UPDATE->DECRYPT->DECRYPT_AND_VERIFY - cipher = AES.new(self.key_128, AES.MODE_OCB, - nonce=self.nonce_96) - cipher.update(self.data) - cipher.decrypt(ct1) - cipher.decrypt_and_verify(ct2, mac) - - def test_invalid_encrypt_after_final(self): - cipher = AES.new(self.key_128, AES.MODE_OCB, - nonce=self.nonce_96) - cipher.update(self.data) - cipher.encrypt(self.data) - cipher.encrypt() - self.assertRaises(TypeError, cipher.encrypt, self.data) - - def test_invalid_decrypt_after_final(self): - cipher = AES.new(self.key_128, AES.MODE_OCB, - nonce=self.nonce_96) - cipher.update(self.data) - cipher.decrypt(self.data) - cipher.decrypt() - self.assertRaises(TypeError, cipher.decrypt, self.data) - - def test_valid_init_digest(self): - # Verify path INIT->DIGEST - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - cipher.digest() - - def test_valid_init_verify(self): - # Verify path INIT->VERIFY - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - mac = cipher.digest() - - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - cipher.verify(mac) - - def test_valid_multiple_encrypt_or_decrypt(self): - for method_name in "encrypt", "decrypt": - for auth_data in (None, b("333"), self.data, - self.data + b("3")): - cipher = AES.new(self.key_128, AES.MODE_OCB, - nonce=self.nonce_96) - if auth_data is not None: - cipher.update(auth_data) - method = getattr(cipher, method_name) - method(self.data) - method(self.data) - method(self.data) - method(self.data) - method() - - def test_valid_multiple_digest_or_verify(self): - # Multiple calls to digest - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - cipher.update(self.data) - first_mac = cipher.digest() - for x in range(4): - self.assertEqual(first_mac, cipher.digest()) - - # Multiple calls to verify - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - cipher.update(self.data) - for x in range(5): - cipher.verify(first_mac) - - def test_valid_encrypt_and_digest_decrypt_and_verify(self): - # encrypt_and_digest - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - cipher.update(self.data) - ct, mac = cipher.encrypt_and_digest(self.data) - - # decrypt_and_verify - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - cipher.update(self.data) - pt = cipher.decrypt_and_verify(ct, mac) - self.assertEqual(self.data, pt) - - def test_invalid_mixing_encrypt_decrypt(self): - # Once per method, with or without assoc. data - for method1_name, method2_name in (("encrypt", "decrypt"), - ("decrypt", "encrypt")): - for assoc_data_present in (True, False): - cipher = AES.new(self.key_128, AES.MODE_OCB, - nonce=self.nonce_96) - if assoc_data_present: - cipher.update(self.data) - getattr(cipher, method1_name)(self.data) - self.assertRaises(TypeError, getattr(cipher, method2_name), - self.data) - - def test_invalid_encrypt_or_update_after_digest(self): - for method_name in "encrypt", "update": - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - cipher.encrypt(self.data) - cipher.encrypt() - cipher.digest() - self.assertRaises(TypeError, getattr(cipher, method_name), - self.data) - - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - cipher.encrypt_and_digest(self.data) - - def test_invalid_decrypt_or_update_after_verify(self): - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - ct = cipher.encrypt(self.data) - ct += cipher.encrypt() - mac = cipher.digest() - - for method_name in "decrypt", "update": - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - cipher.decrypt(ct) - cipher.decrypt() - cipher.verify(mac) - self.assertRaises(TypeError, getattr(cipher, method_name), - self.data) - - cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) - cipher.decrypt_and_verify(ct, mac) - self.assertRaises(TypeError, getattr(cipher, method_name), - self.data) - - -def algo_rfc7253(keylen, taglen, noncelen): - """Implement the algorithm at page 18 of RFC 7253""" - - key = bchr(0) * (keylen // 8 - 1) + bchr(taglen) - C = b"" - - for i in range(128): - S = bchr(0) * i - - N = long_to_bytes(3 * i + 1, noncelen // 8) - cipher = AES.new(key, AES.MODE_OCB, nonce=N, mac_len=taglen // 8) - cipher.update(S) - C += cipher.encrypt(S) + cipher.encrypt() + cipher.digest() - - N = long_to_bytes(3 * i + 2, noncelen // 8) - cipher = AES.new(key, AES.MODE_OCB, nonce=N, mac_len=taglen // 8) - C += cipher.encrypt(S) + cipher.encrypt() + cipher.digest() - - N = long_to_bytes(3 * i + 3, noncelen // 8) - cipher = AES.new(key, AES.MODE_OCB, nonce=N, mac_len=taglen // 8) - cipher.update(S) - C += cipher.encrypt() + cipher.digest() - - N = long_to_bytes(385, noncelen // 8) - cipher = AES.new(key, AES.MODE_OCB, nonce=N, mac_len=taglen // 8) - cipher.update(C) - return cipher.encrypt() + cipher.digest() - - -class OcbRfc7253Test(unittest.TestCase): - - # Tuple with - # - nonce - # - authenticated data - # - plaintext - # - ciphertext and 16 byte MAC tag - tv1_key = "000102030405060708090A0B0C0D0E0F" - tv1 = ( - ( - "BBAA99887766554433221100", - "", - "", - "785407BFFFC8AD9EDCC5520AC9111EE6" - ), - ( - "BBAA99887766554433221101", - "0001020304050607", - "0001020304050607", - "6820B3657B6F615A5725BDA0D3B4EB3A257C9AF1F8F03009" - ), - ( - "BBAA99887766554433221102", - "0001020304050607", - "", - "81017F8203F081277152FADE694A0A00" - ), - ( - "BBAA99887766554433221103", - "", - "0001020304050607", - "45DD69F8F5AAE72414054CD1F35D82760B2CD00D2F99BFA9" - ), - ( - "BBAA99887766554433221104", - "000102030405060708090A0B0C0D0E0F", - "000102030405060708090A0B0C0D0E0F", - "571D535B60B277188BE5147170A9A22C3AD7A4FF3835B8C5" - "701C1CCEC8FC3358" - ), - ( - "BBAA99887766554433221105", - "000102030405060708090A0B0C0D0E0F", - "", - "8CF761B6902EF764462AD86498CA6B97" - ), - ( - "BBAA99887766554433221106", - "", - "000102030405060708090A0B0C0D0E0F", - "5CE88EC2E0692706A915C00AEB8B2396F40E1C743F52436B" - "DF06D8FA1ECA343D" - ), - ( - "BBAA99887766554433221107", - "000102030405060708090A0B0C0D0E0F1011121314151617", - "000102030405060708090A0B0C0D0E0F1011121314151617", - "1CA2207308C87C010756104D8840CE1952F09673A448A122" - "C92C62241051F57356D7F3C90BB0E07F" - ), - ( - "BBAA99887766554433221108", - "000102030405060708090A0B0C0D0E0F1011121314151617", - "", - "6DC225A071FC1B9F7C69F93B0F1E10DE" - ), - ( - "BBAA99887766554433221109", - "", - "000102030405060708090A0B0C0D0E0F1011121314151617", - "221BD0DE7FA6FE993ECCD769460A0AF2D6CDED0C395B1C3C" - "E725F32494B9F914D85C0B1EB38357FF" - ), - ( - "BBAA9988776655443322110A", - "000102030405060708090A0B0C0D0E0F1011121314151617" - "18191A1B1C1D1E1F", - "000102030405060708090A0B0C0D0E0F1011121314151617" - "18191A1B1C1D1E1F", - "BD6F6C496201C69296C11EFD138A467ABD3C707924B964DE" - "AFFC40319AF5A48540FBBA186C5553C68AD9F592A79A4240" - ), - ( - "BBAA9988776655443322110B", - "000102030405060708090A0B0C0D0E0F1011121314151617" - "18191A1B1C1D1E1F", - "", - "FE80690BEE8A485D11F32965BC9D2A32" - ), - ( - "BBAA9988776655443322110C", - "", - "000102030405060708090A0B0C0D0E0F1011121314151617" - "18191A1B1C1D1E1F", - "2942BFC773BDA23CABC6ACFD9BFD5835BD300F0973792EF4" - "6040C53F1432BCDFB5E1DDE3BC18A5F840B52E653444D5DF" - ), - ( - "BBAA9988776655443322110D", - "000102030405060708090A0B0C0D0E0F1011121314151617" - "18191A1B1C1D1E1F2021222324252627", - "000102030405060708090A0B0C0D0E0F1011121314151617" - "18191A1B1C1D1E1F2021222324252627", - "D5CA91748410C1751FF8A2F618255B68A0A12E093FF45460" - "6E59F9C1D0DDC54B65E8628E568BAD7AED07BA06A4A69483" - "A7035490C5769E60" - ), - ( - "BBAA9988776655443322110E", - "000102030405060708090A0B0C0D0E0F1011121314151617" - "18191A1B1C1D1E1F2021222324252627", - "", - "C5CD9D1850C141E358649994EE701B68" - ), - ( - "BBAA9988776655443322110F", - "", - "000102030405060708090A0B0C0D0E0F1011121314151617" - "18191A1B1C1D1E1F2021222324252627", - "4412923493C57D5DE0D700F753CCE0D1D2D95060122E9F15" - "A5DDBFC5787E50B5CC55EE507BCB084E479AD363AC366B95" - "A98CA5F3000B1479" - ) - ) - - # Tuple with - # - key - # - nonce - # - authenticated data - # - plaintext - # - ciphertext and 12 byte MAC tag - tv2 = ( - "0F0E0D0C0B0A09080706050403020100", - "BBAA9988776655443322110D", - "000102030405060708090A0B0C0D0E0F1011121314151617" - "18191A1B1C1D1E1F2021222324252627", - "000102030405060708090A0B0C0D0E0F1011121314151617" - "18191A1B1C1D1E1F2021222324252627", - "1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1" - "A0124B0A55BAE884ED93481529C76B6AD0C515F4D1CDD4FD" - "AC4F02AA" - ) - - # Tuple with - # - key length - # - MAC tag length - # - Expected output - tv3 = ( - (128, 128, "67E944D23256C5E0B6C61FA22FDF1EA2"), - (192, 128, "F673F2C3E7174AAE7BAE986CA9F29E17"), - (256, 128, "D90EB8E9C977C88B79DD793D7FFA161C"), - (128, 96, "77A3D8E73589158D25D01209"), - (192, 96, "05D56EAD2752C86BE6932C5E"), - (256, 96, "5458359AC23B0CBA9E6330DD"), - (128, 64, "192C9B7BD90BA06A"), - (192, 64, "0066BC6E0EF34E24"), - (256, 64, "7D4EA5D445501CBE"), - ) - - def test1(self): - key = unhexlify(b(self.tv1_key)) - for tv in self.tv1: - nonce, aad, pt, ct = [unhexlify(b(x)) for x in tv] - ct, mac_tag = ct[:-16], ct[-16:] - - cipher = AES.new(key, AES.MODE_OCB, nonce=nonce) - cipher.update(aad) - ct2 = cipher.encrypt(pt) + cipher.encrypt() - self.assertEqual(ct, ct2) - self.assertEqual(mac_tag, cipher.digest()) - - cipher = AES.new(key, AES.MODE_OCB, nonce=nonce) - cipher.update(aad) - pt2 = cipher.decrypt(ct) + cipher.decrypt() - self.assertEqual(pt, pt2) - cipher.verify(mac_tag) - - def test2(self): - - key, nonce, aad, pt, ct = [unhexlify(b(x)) for x in self.tv2] - ct, mac_tag = ct[:-12], ct[-12:] - - cipher = AES.new(key, AES.MODE_OCB, nonce=nonce, mac_len=12) - cipher.update(aad) - ct2 = cipher.encrypt(pt) + cipher.encrypt() - self.assertEqual(ct, ct2) - self.assertEqual(mac_tag, cipher.digest()) - - cipher = AES.new(key, AES.MODE_OCB, nonce=nonce, mac_len=12) - cipher.update(aad) - pt2 = cipher.decrypt(ct) + cipher.decrypt() - self.assertEqual(pt, pt2) - cipher.verify(mac_tag) - - def test3(self): - for keylen, taglen, result in self.tv3: - result2 = algo_rfc7253(keylen, taglen, 96) - self.assertEqual(unhexlify(b(result)), result2) - - -class OcbDkgTest(unittest.TestCase): - """Test vectors from https://gitlab.com/dkg/ocb-test-vectors""" - - def test_1_2(self): - tvs = [] - for fi in (1, 2): - for nb in (104, 112, 120): - tv_file = load_test_vectors(("Cipher", "AES"), - "test-vector-%d-nonce%d.txt" % (fi, nb), - "DKG tests, %d, %d bits" % (fi, nb), - {}) - if tv_file is None: - break - key = tv_file[0].k - for tv in tv_file[1:]: - tv.k = key - tvs.append(tv) - - for tv in tvs: - k, n, a, p, c = tv.k, tv.n, tv.a, tv.p, tv.c - mac_len = len(c) - len(p) - cipher = AES.new(k, AES.MODE_OCB, nonce=n, mac_len=mac_len) - cipher.update(a) - c_out, tag_out = cipher.encrypt_and_digest(p) - self.assertEqual(c, c_out + tag_out) - - def test_3(self): - - def check(keylen, taglen, noncelen, exp): - result = algo_rfc7253(keylen, taglen, noncelen) - self.assertEqual(result, unhexlify(exp)) - - # test-vector-3-nonce104.txt - check(128, 128, 104, "C47F5F0341E15326D4D1C46F47F05062") - check(192, 128, 104, "95B9167A38EB80495DFC561A8486E109") - check(256, 128, 104, "AFE1CDDB97028FD92F8FB3C8CFBA7D83") - check(128, 96, 104, "F471B4983BA80946DF217A54") - check(192, 96, 104, "5AE828BC51C24D85FA5CC7B2") - check(256, 96, 104, "8C8335982E2B734616CAD14C") - check(128, 64, 104, "B553F74B85FD1E5B") - check(192, 64, 104, "3B49D20E513531F9") - check(256, 64, 104, "ED6DA5B1216BF8BB") - - # test-vector-3-nonce112.txt - check(128, 128, 112, "CA8AFCA031BAC3F480A583BD6C50A547") - check(192, 128, 112, "D170C1DF356308079DA9A3F619147148") - check(256, 128, 112, "57F94381F2F9231EFB04AECD323757C3") - check(128, 96, 112, "3A618B2531ED39F260C750DC") - check(192, 96, 112, "9071EB89FEDBADDA88FD286E") - check(256, 96, 112, "FDF0EFB97F21A39AC4BAB5AC") - check(128, 64, 112, "FAB2FF3A8DD82A13") - check(192, 64, 112, "AC01D912BD0737D3") - check(256, 64, 112, "9D1FD0B500EA4ECF") - - # test-vector-3-nonce120.txt - check(128, 128, 120, "9E043A7140A25FB91F43BCC9DD7E0F46") - check(192, 128, 120, "680000E53908323A7F396B955B8EC641") - check(256, 128, 120, "8304B97FAACDA56E676602E1878A7E6F") - check(128, 96, 120, "81F978AC9867E825D339847D") - check(192, 96, 120, "EFCF2D60B24926ADA48CF5B1") - check(256, 96, 120, "84961DC56E917B165E58C174") - check(128, 64, 120, "227AEE6C9D905A61") - check(192, 64, 120, "541DE691B9E1A2F9") - check(256, 64, 120, "B0E761381C7129FC") - - def test_2_bugfix(self): - nonce = unhexlify("EEDDCCBBAA9988776655443322110D") - key = unhexlify("0F0E0D0C0B0A09080706050403020100") - A = unhexlify("000102030405060708090A0B0C0D0E0F1011121314151617" - "18191A1B1C1D1E1F2021222324252627") - P = unhexlify("000102030405060708090A0B0C0D0E0F1011121314151617" - "18191A1B1C1D1E1F2021222324252627") - C = unhexlify("07E903BFC49552411ABC865F5ECE60F6FAD1F5A9F14D3070" - "FA2F1308A563207FFE14C1EEA44B22059C7484319D8A2C53" - "C236A7B3") - mac_len = len(C) - len(P) - - # Prior to version 3.17, a nonce of maximum length (15 bytes) - # was actually used as a 14 byte nonce. The last byte was erroneously - # ignored. - buggy_result = unhexlify("BA015C4E5AE54D76C890AE81BD40DC57" - "03EDC30E8AC2A58BC5D8FA4D61C5BAE6" - "C39BEAC435B2FD56A2A5085C1B135D77" - "0C8264B7") - cipher = AES.new(key, AES.MODE_OCB, nonce=nonce[:-1], mac_len=mac_len) - cipher.update(A) - C_out2, tag_out2 = cipher.encrypt_and_digest(P) - self.assertEqual(buggy_result, C_out2 + tag_out2) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(OcbTests) - tests += list_test_cases(OcbFSMTests) - tests += list_test_cases(OcbRfc7253Test) - tests += list_test_cases(OcbDkgTest) - return tests - - -if __name__ == '__main__': - def suite(): - return unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_OFB.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_OFB.py deleted file mode 100644 index 9a8ef0a..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_OFB.py +++ /dev/null @@ -1,238 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2015, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import unittest -from binascii import unhexlify - -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.Util.py3compat import tobytes -from Cryptodome.Cipher import AES, DES3, DES -from Cryptodome.Hash import SHAKE128 -from Cryptodome.SelfTest.loader import load_test_vectors_wycheproof - -def get_tag_random(tag, length): - return SHAKE128.new(data=tobytes(tag)).read(length) - -from Cryptodome.SelfTest.Cipher.test_CBC import BlockChainingTests - -class OfbTests(BlockChainingTests): - - aes_mode = AES.MODE_OFB - des3_mode = DES3.MODE_OFB - - # Redefine test_unaligned_data_128/64 - - def test_unaligned_data_128(self): - plaintexts = [ b"7777777" ] * 100 - - cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=8) - ciphertexts = [ cipher.encrypt(x) for x in plaintexts ] - cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=8) - self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts))) - - cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=128) - ciphertexts = [ cipher.encrypt(x) for x in plaintexts ] - cipher = AES.new(self.key_128, AES.MODE_CFB, self.iv_128, segment_size=128) - self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts))) - - def test_unaligned_data_64(self): - plaintexts = [ b"7777777" ] * 100 - cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=8) - ciphertexts = [ cipher.encrypt(x) for x in plaintexts ] - cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=8) - self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts))) - - cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=64) - ciphertexts = [ cipher.encrypt(x) for x in plaintexts ] - cipher = DES3.new(self.key_192, DES3.MODE_CFB, self.iv_64, segment_size=64) - self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts))) - - -from Cryptodome.SelfTest.Cipher.test_CBC import NistBlockChainingVectors - -class NistOfbVectors(NistBlockChainingVectors): - aes_mode = AES.MODE_OFB - des_mode = DES.MODE_OFB - des3_mode = DES3.MODE_OFB - - -# Create one test method per file -nist_aes_kat_mmt_files = ( - # KAT - "OFBGFSbox128.rsp", - "OFBGFSbox192.rsp", - "OFBGFSbox256.rsp", - "OFBKeySbox128.rsp", - "OFBKeySbox192.rsp", - "OFBKeySbox256.rsp", - "OFBVarKey128.rsp", - "OFBVarKey192.rsp", - "OFBVarKey256.rsp", - "OFBVarTxt128.rsp", - "OFBVarTxt192.rsp", - "OFBVarTxt256.rsp", - # MMT - "OFBMMT128.rsp", - "OFBMMT192.rsp", - "OFBMMT256.rsp", - ) -nist_aes_mct_files = ( - "OFBMCT128.rsp", - "OFBMCT192.rsp", - "OFBMCT256.rsp", - ) - -for file_name in nist_aes_kat_mmt_files: - def new_func(self, file_name=file_name): - self._do_kat_aes_test(file_name) - setattr(NistOfbVectors, "test_AES_" + file_name, new_func) - -for file_name in nist_aes_mct_files: - def new_func(self, file_name=file_name): - self._do_mct_aes_test(file_name) - setattr(NistOfbVectors, "test_AES_" + file_name, new_func) -del file_name, new_func - -nist_tdes_files = ( - "TOFBMMT2.rsp", # 2TDES - "TOFBMMT3.rsp", # 3TDES - "TOFBinvperm.rsp", # Single DES - "TOFBpermop.rsp", - "TOFBsubtab.rsp", - "TOFBvarkey.rsp", - "TOFBvartext.rsp", - ) - -for file_name in nist_tdes_files: - def new_func(self, file_name=file_name): - self._do_tdes_test(file_name) - setattr(NistOfbVectors, "test_TDES_" + file_name, new_func) - -# END OF NIST OFB TEST VECTORS - - -class SP800TestVectors(unittest.TestCase): - """Class exercising the OFB test vectors found in Section F.4 - of NIST SP 800-3A""" - - def test_aes_128(self): - plaintext = '6bc1bee22e409f96e93d7e117393172a' +\ - 'ae2d8a571e03ac9c9eb76fac45af8e51' +\ - '30c81c46a35ce411e5fbc1191a0a52ef' +\ - 'f69f2445df4f9b17ad2b417be66c3710' - ciphertext = '3b3fd92eb72dad20333449f8e83cfb4a' +\ - '7789508d16918f03f53c52dac54ed825' +\ - '9740051e9c5fecf64344f7a82260edcc' +\ - '304c6528f659c77866a510d9c1d6ae5e' - key = '2b7e151628aed2a6abf7158809cf4f3c' - iv = '000102030405060708090a0b0c0d0e0f' - - key = unhexlify(key) - iv = unhexlify(iv) - plaintext = unhexlify(plaintext) - ciphertext = unhexlify(ciphertext) - - cipher = AES.new(key, AES.MODE_OFB, iv) - self.assertEqual(cipher.encrypt(plaintext), ciphertext) - cipher = AES.new(key, AES.MODE_OFB, iv) - self.assertEqual(cipher.decrypt(ciphertext), plaintext) - - cipher = AES.new(key, AES.MODE_OFB, iv) - self.assertEqual(cipher.encrypt(plaintext[:-8]), ciphertext[:-8]) - cipher = AES.new(key, AES.MODE_OFB, iv) - self.assertEqual(cipher.decrypt(ciphertext[:-8]), plaintext[:-8]) - - def test_aes_192(self): - plaintext = '6bc1bee22e409f96e93d7e117393172a' +\ - 'ae2d8a571e03ac9c9eb76fac45af8e51' +\ - '30c81c46a35ce411e5fbc1191a0a52ef' +\ - 'f69f2445df4f9b17ad2b417be66c3710' - ciphertext = 'cdc80d6fddf18cab34c25909c99a4174' +\ - 'fcc28b8d4c63837c09e81700c1100401' +\ - '8d9a9aeac0f6596f559c6d4daf59a5f2' +\ - '6d9f200857ca6c3e9cac524bd9acc92a' - key = '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b' - iv = '000102030405060708090a0b0c0d0e0f' - - key = unhexlify(key) - iv = unhexlify(iv) - plaintext = unhexlify(plaintext) - ciphertext = unhexlify(ciphertext) - - cipher = AES.new(key, AES.MODE_OFB, iv) - self.assertEqual(cipher.encrypt(plaintext), ciphertext) - cipher = AES.new(key, AES.MODE_OFB, iv) - self.assertEqual(cipher.decrypt(ciphertext), plaintext) - - cipher = AES.new(key, AES.MODE_OFB, iv) - self.assertEqual(cipher.encrypt(plaintext[:-8]), ciphertext[:-8]) - cipher = AES.new(key, AES.MODE_OFB, iv) - self.assertEqual(cipher.decrypt(ciphertext[:-8]), plaintext[:-8]) - - def test_aes_256(self): - plaintext = '6bc1bee22e409f96e93d7e117393172a' +\ - 'ae2d8a571e03ac9c9eb76fac45af8e51' +\ - '30c81c46a35ce411e5fbc1191a0a52ef' +\ - 'f69f2445df4f9b17ad2b417be66c3710' - ciphertext = 'dc7e84bfda79164b7ecd8486985d3860' +\ - '4febdc6740d20b3ac88f6ad82a4fb08d' +\ - '71ab47a086e86eedf39d1c5bba97c408' +\ - '0126141d67f37be8538f5a8be740e484' - key = '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4' - iv = '000102030405060708090a0b0c0d0e0f' - - key = unhexlify(key) - iv = unhexlify(iv) - plaintext = unhexlify(plaintext) - ciphertext = unhexlify(ciphertext) - - cipher = AES.new(key, AES.MODE_OFB, iv) - self.assertEqual(cipher.encrypt(plaintext), ciphertext) - cipher = AES.new(key, AES.MODE_OFB, iv) - self.assertEqual(cipher.decrypt(ciphertext), plaintext) - - cipher = AES.new(key, AES.MODE_OFB, iv) - self.assertEqual(cipher.encrypt(plaintext[:-8]), ciphertext[:-8]) - cipher = AES.new(key, AES.MODE_OFB, iv) - self.assertEqual(cipher.decrypt(ciphertext[:-8]), plaintext[:-8]) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(OfbTests) - if config.get('slow_tests'): - tests += list_test_cases(NistOfbVectors) - tests += list_test_cases(SP800TestVectors) - return tests - - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_OpenPGP.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_OpenPGP.py deleted file mode 100644 index 4090a1a..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_OpenPGP.py +++ /dev/null @@ -1,218 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2015, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import unittest -from binascii import unhexlify - -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.Util.py3compat import tobytes -from Cryptodome.Cipher import AES, DES3, DES -from Cryptodome.Hash import SHAKE128 - -def get_tag_random(tag, length): - return SHAKE128.new(data=tobytes(tag)).read(length) - - -from Cryptodome.SelfTest.Cipher.test_CBC import BlockChainingTests - -class OpenPGPTests(BlockChainingTests): - - aes_mode = AES.MODE_OPENPGP - des3_mode = DES3.MODE_OPENPGP - - # Redefine test_unaligned_data_128/64 - - key_128 = get_tag_random("key_128", 16) - key_192 = get_tag_random("key_192", 24) - iv_128 = get_tag_random("iv_128", 16) - iv_64 = get_tag_random("iv_64", 8) - data_128 = get_tag_random("data_128", 16) - - def test_loopback_128(self): - cipher = AES.new(self.key_128, AES.MODE_OPENPGP, self.iv_128) - pt = get_tag_random("plaintext", 16 * 100) - ct = cipher.encrypt(pt) - - eiv, ct = ct[:18], ct[18:] - - cipher = AES.new(self.key_128, AES.MODE_OPENPGP, eiv) - pt2 = cipher.decrypt(ct) - self.assertEqual(pt, pt2) - - def test_loopback_64(self): - cipher = DES3.new(self.key_192, DES3.MODE_OPENPGP, self.iv_64) - pt = get_tag_random("plaintext", 8 * 100) - ct = cipher.encrypt(pt) - - eiv, ct = ct[:10], ct[10:] - - cipher = DES3.new(self.key_192, DES3.MODE_OPENPGP, eiv) - pt2 = cipher.decrypt(ct) - self.assertEqual(pt, pt2) - - def test_IV_iv_attributes(self): - cipher = AES.new(self.key_128, AES.MODE_OPENPGP, self.iv_128) - eiv = cipher.encrypt(b"") - self.assertEqual(cipher.iv, self.iv_128) - - cipher = AES.new(self.key_128, AES.MODE_OPENPGP, eiv) - self.assertEqual(cipher.iv, self.iv_128) - - def test_null_encryption_decryption(self): - cipher = AES.new(self.key_128, AES.MODE_OPENPGP, self.iv_128) - eiv = cipher.encrypt(b"") - - cipher = AES.new(self.key_128, AES.MODE_OPENPGP, eiv) - self.assertEqual(cipher.decrypt(b""), b"") - - def test_either_encrypt_or_decrypt(self): - cipher = AES.new(self.key_128, AES.MODE_OPENPGP, self.iv_128) - eiv = cipher.encrypt(b"") - self.assertRaises(TypeError, cipher.decrypt, b"") - - cipher = AES.new(self.key_128, AES.MODE_OPENPGP, eiv) - cipher.decrypt(b"") - self.assertRaises(TypeError, cipher.encrypt, b"") - - def test_unaligned_data_128(self): - plaintexts = [ b"7777777" ] * 100 - - cipher = AES.new(self.key_128, AES.MODE_OPENPGP, self.iv_128) - ciphertexts = [ cipher.encrypt(x) for x in plaintexts ] - cipher = AES.new(self.key_128, AES.MODE_OPENPGP, self.iv_128) - self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts))) - - def test_unaligned_data_64(self): - plaintexts = [ b"7777777" ] * 100 - - cipher = DES3.new(self.key_192, DES3.MODE_OPENPGP, self.iv_64) - ciphertexts = [ cipher.encrypt(x) for x in plaintexts ] - cipher = DES3.new(self.key_192, DES3.MODE_OPENPGP, self.iv_64) - self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts))) - - def test_output_param(self): - pass - - def test_output_param_same_buffer(self): - pass - - def test_output_param_memoryview(self): - pass - - def test_output_param_neg(self): - pass - - -class TestVectors(unittest.TestCase): - - def test_aes(self): - # The following test vectors have been generated with gpg v1.4.0. - # The command line used was: - # - # gpg -c -z 0 --cipher-algo AES --passphrase secret_passphrase \ - # --disable-mdc --s2k-mode 0 --output ct pt - # - # As result, the content of the file 'pt' is encrypted with a key derived - # from 'secret_passphrase' and written to file 'ct'. - # Test vectors must be extracted from 'ct', which is a collection of - # TLVs (see RFC4880 for all details): - # - the encrypted data (with the encrypted IV as prefix) is the payload - # of the TLV with tag 9 (Symmetrical Encrypted Data Packet). - # This is the ciphertext in the test vector. - # - inside the encrypted part, there is a further layer of TLVs. One must - # look for tag 11 (Literal Data Packet); in its payload, after a short - # but time dependent header, there is the content of file 'pt'. - # In the test vector, the plaintext is the complete set of TLVs that gets - # encrypted. It is not just the content of 'pt'. - # - the key is the leftmost 16 bytes of the SHA1 digest of the password. - # The test vector contains such shortened digest. - # - # Note that encryption uses a clear IV, and decryption an encrypted IV - - plaintext = 'ac18620270744fb4f647426c61636b4361745768697465436174' - ciphertext = 'dc6b9e1f095de609765c59983db5956ae4f63aea7405389d2ebb' - key = '5baa61e4c9b93f3f0682250b6cf8331b' - iv = '3d7d3e62282add7eb203eeba5c800733' - encrypted_iv='fd934601ef49cb58b6d9aebca6056bdb96ef' - - plaintext = unhexlify(plaintext) - ciphertext = unhexlify(ciphertext) - key = unhexlify(key) - iv = unhexlify(iv) - encrypted_iv = unhexlify(encrypted_iv) - - cipher = AES.new(key, AES.MODE_OPENPGP, iv) - ct = cipher.encrypt(plaintext) - self.assertEqual(ct[:18], encrypted_iv) - self.assertEqual(ct[18:], ciphertext) - - cipher = AES.new(key, AES.MODE_OPENPGP, encrypted_iv) - pt = cipher.decrypt(ciphertext) - self.assertEqual(pt, plaintext) - - def test_des3(self): - # The following test vectors have been generated with gpg v1.4.0. - # The command line used was: - # gpg -c -z 0 --cipher-algo 3DES --passphrase secret_passphrase \ - # --disable-mdc --s2k-mode 0 --output ct pt - # For an explanation, see test_AES.py . - - plaintext = 'ac1762037074324fb53ba3596f73656d69746556616c6c6579' - ciphertext = '9979238528357b90e2e0be549cb0b2d5999b9a4a447e5c5c7d' - key = '7ade65b460f5ea9be35f9e14aa883a2048e3824aa616c0b2' - iv='cd47e2afb8b7e4b0' - encrypted_iv='6a7eef0b58050e8b904a' - - plaintext = unhexlify(plaintext) - ciphertext = unhexlify(ciphertext) - key = unhexlify(key) - iv = unhexlify(iv) - encrypted_iv = unhexlify(encrypted_iv) - - cipher = DES3.new(key, DES3.MODE_OPENPGP, iv) - ct = cipher.encrypt(plaintext) - self.assertEqual(ct[:10], encrypted_iv) - self.assertEqual(ct[10:], ciphertext) - - cipher = DES3.new(key, DES3.MODE_OPENPGP, encrypted_iv) - pt = cipher.decrypt(ciphertext) - self.assertEqual(pt, plaintext) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(OpenPGPTests) - tests += list_test_cases(TestVectors) - return tests - - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_SIV.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_SIV.py deleted file mode 100644 index d4bb5a9..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_SIV.py +++ /dev/null @@ -1,552 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2015, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import json -import unittest -from binascii import unhexlify - -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.SelfTest.loader import load_test_vectors_wycheproof - -from Cryptodome.Util.py3compat import tobytes, bchr -from Cryptodome.Cipher import AES -from Cryptodome.Hash import SHAKE128 - -from Cryptodome.Util.strxor import strxor - - -def get_tag_random(tag, length): - return SHAKE128.new(data=tobytes(tag)).read(length) - - -class SivTests(unittest.TestCase): - - key_256 = get_tag_random("key_256", 32) - key_384 = get_tag_random("key_384", 48) - key_512 = get_tag_random("key_512", 64) - nonce_96 = get_tag_random("nonce_128", 12) - data = get_tag_random("data", 128) - - def test_loopback_128(self): - for key in self.key_256, self.key_384, self.key_512: - cipher = AES.new(key, AES.MODE_SIV, nonce=self.nonce_96) - pt = get_tag_random("plaintext", 16 * 100) - ct, mac = cipher.encrypt_and_digest(pt) - - cipher = AES.new(key, AES.MODE_SIV, nonce=self.nonce_96) - pt2 = cipher.decrypt_and_verify(ct, mac) - self.assertEqual(pt, pt2) - - def test_nonce(self): - # Deterministic encryption - AES.new(self.key_256, AES.MODE_SIV) - - cipher = AES.new(self.key_256, AES.MODE_SIV, self.nonce_96) - ct1, tag1 = cipher.encrypt_and_digest(self.data) - - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - ct2, tag2 = cipher.encrypt_and_digest(self.data) - self.assertEqual(ct1 + tag1, ct2 + tag2) - - def test_nonce_must_be_bytes(self): - self.assertRaises(TypeError, AES.new, self.key_256, AES.MODE_SIV, - nonce=u'test12345678') - - def test_nonce_length(self): - # nonce can be of any length (but not empty) - self.assertRaises(ValueError, AES.new, self.key_256, AES.MODE_SIV, - nonce=b"") - - for x in range(1, 128): - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=bchr(1) * x) - cipher.encrypt_and_digest(b'\x01') - - def test_block_size_128(self): - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - self.assertEqual(cipher.block_size, AES.block_size) - - def test_nonce_attribute(self): - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - self.assertEqual(cipher.nonce, self.nonce_96) - - # By default, no nonce is randomly generated - self.assertFalse(hasattr(AES.new(self.key_256, AES.MODE_SIV), "nonce")) - - def test_unknown_parameters(self): - self.assertRaises(TypeError, AES.new, self.key_256, AES.MODE_SIV, - self.nonce_96, 7) - self.assertRaises(TypeError, AES.new, self.key_256, AES.MODE_SIV, - nonce=self.nonce_96, unknown=7) - - # But some are only known by the base cipher - # (e.g. use_aesni consumed by the AES module) - AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96, - use_aesni=False) - - def test_encrypt_excludes_decrypt(self): - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - cipher.encrypt_and_digest(self.data) - self.assertRaises(TypeError, cipher.decrypt, self.data) - - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - cipher.encrypt_and_digest(self.data) - self.assertRaises(TypeError, cipher.decrypt_and_verify, - self.data, self.data) - - def test_data_must_be_bytes(self): - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.encrypt, u'test1234567890-*') - - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.decrypt_and_verify, - u'test1234567890-*', b"xxxx") - - def test_mac_len(self): - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - _, mac = cipher.encrypt_and_digest(self.data) - self.assertEqual(len(mac), 16) - - def test_invalid_mac(self): - from Cryptodome.Util.strxor import strxor_c - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - ct, mac = cipher.encrypt_and_digest(self.data) - - invalid_mac = strxor_c(mac, 0x01) - - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - self.assertRaises(ValueError, cipher.decrypt_and_verify, ct, - invalid_mac) - - def test_hex_mac(self): - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - mac_hex = cipher.hexdigest() - self.assertEqual(cipher.digest(), unhexlify(mac_hex)) - - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - cipher.hexverify(mac_hex) - - def test_bytearray(self): - - # Encrypt - key = bytearray(self.key_256) - nonce = bytearray(self.nonce_96) - data = bytearray(self.data) - header = bytearray(self.data) - - cipher1 = AES.new(self.key_256, - AES.MODE_SIV, - nonce=self.nonce_96) - cipher1.update(self.data) - ct, tag = cipher1.encrypt_and_digest(self.data) - - cipher2 = AES.new(key, - AES.MODE_SIV, - nonce=nonce) - key[:3] = b'\xFF\xFF\xFF' - nonce[:3] = b'\xFF\xFF\xFF' - cipher2.update(header) - header[:3] = b'\xFF\xFF\xFF' - ct_test, tag_test = cipher2.encrypt_and_digest(data) - - self.assertEqual(ct, ct_test) - self.assertEqual(tag, tag_test) - self.assertEqual(cipher1.nonce, cipher2.nonce) - - # Decrypt - key = bytearray(self.key_256) - nonce = bytearray(self.nonce_96) - header = bytearray(self.data) - ct_ba = bytearray(ct) - tag_ba = bytearray(tag) - - cipher3 = AES.new(key, - AES.MODE_SIV, - nonce=nonce) - key[:3] = b'\xFF\xFF\xFF' - nonce[:3] = b'\xFF\xFF\xFF' - cipher3.update(header) - header[:3] = b'\xFF\xFF\xFF' - pt_test = cipher3.decrypt_and_verify(ct_ba, tag_ba) - - self.assertEqual(self.data, pt_test) - - def test_memoryview(self): - - # Encrypt - key = memoryview(bytearray(self.key_256)) - nonce = memoryview(bytearray(self.nonce_96)) - data = memoryview(bytearray(self.data)) - header = memoryview(bytearray(self.data)) - - cipher1 = AES.new(self.key_256, - AES.MODE_SIV, - nonce=self.nonce_96) - cipher1.update(self.data) - ct, tag = cipher1.encrypt_and_digest(self.data) - - cipher2 = AES.new(key, - AES.MODE_SIV, - nonce=nonce) - key[:3] = b'\xFF\xFF\xFF' - nonce[:3] = b'\xFF\xFF\xFF' - cipher2.update(header) - header[:3] = b'\xFF\xFF\xFF' - ct_test, tag_test= cipher2.encrypt_and_digest(data) - - self.assertEqual(ct, ct_test) - self.assertEqual(tag, tag_test) - self.assertEqual(cipher1.nonce, cipher2.nonce) - - # Decrypt - key = memoryview(bytearray(self.key_256)) - nonce = memoryview(bytearray(self.nonce_96)) - header = memoryview(bytearray(self.data)) - ct_ba = memoryview(bytearray(ct)) - tag_ba = memoryview(bytearray(tag)) - - cipher3 = AES.new(key, - AES.MODE_SIV, - nonce=nonce) - key[:3] = b'\xFF\xFF\xFF' - nonce[:3] = b'\xFF\xFF\xFF' - cipher3.update(header) - header[:3] = b'\xFF\xFF\xFF' - pt_test = cipher3.decrypt_and_verify(ct_ba, tag_ba) - - self.assertEqual(self.data, pt_test) - - def test_output_param(self): - - pt = b'5' * 128 - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - ct, tag = cipher.encrypt_and_digest(pt) - - output = bytearray(128) - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - res, tag_out = cipher.encrypt_and_digest(pt, output=output) - self.assertEqual(ct, output) - self.assertEqual(res, None) - self.assertEqual(tag, tag_out) - - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - res = cipher.decrypt_and_verify(ct, tag, output=output) - self.assertEqual(pt, output) - self.assertEqual(res, None) - - def test_output_param_memoryview(self): - - pt = b'5' * 128 - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - ct, tag = cipher.encrypt_and_digest(pt) - - output = memoryview(bytearray(128)) - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - cipher.encrypt_and_digest(pt, output=output) - self.assertEqual(ct, output) - - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - cipher.decrypt_and_verify(ct, tag, output=output) - self.assertEqual(pt, output) - - def test_output_param_neg(self): - LEN_PT = 128 - - pt = b'5' * LEN_PT - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - ct, tag = cipher.encrypt_and_digest(pt) - - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.encrypt_and_digest, pt, output=b'0' * LEN_PT) - - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.decrypt_and_verify, ct, tag, output=b'0' * LEN_PT) - - shorter_output = bytearray(LEN_PT - 1) - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - self.assertRaises(ValueError, cipher.encrypt_and_digest, pt, output=shorter_output) - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - self.assertRaises(ValueError, cipher.decrypt_and_verify, ct, tag, output=shorter_output) - - -class SivFSMTests(unittest.TestCase): - - key_256 = get_tag_random("key_256", 32) - nonce_96 = get_tag_random("nonce_96", 12) - data = get_tag_random("data", 128) - - def test_invalid_init_encrypt(self): - # Path INIT->ENCRYPT fails - cipher = AES.new(self.key_256, AES.MODE_SIV, - nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.encrypt, b"xxx") - - def test_invalid_init_decrypt(self): - # Path INIT->DECRYPT fails - cipher = AES.new(self.key_256, AES.MODE_SIV, - nonce=self.nonce_96) - self.assertRaises(TypeError, cipher.decrypt, b"xxx") - - def test_valid_init_update_digest_verify(self): - # No plaintext, fixed authenticated data - # Verify path INIT->UPDATE->DIGEST - cipher = AES.new(self.key_256, AES.MODE_SIV, - nonce=self.nonce_96) - cipher.update(self.data) - mac = cipher.digest() - - # Verify path INIT->UPDATE->VERIFY - cipher = AES.new(self.key_256, AES.MODE_SIV, - nonce=self.nonce_96) - cipher.update(self.data) - cipher.verify(mac) - - def test_valid_init_digest(self): - # Verify path INIT->DIGEST - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - cipher.digest() - - def test_valid_init_verify(self): - # Verify path INIT->VERIFY - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - mac = cipher.digest() - - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - cipher.verify(mac) - - def test_valid_multiple_digest_or_verify(self): - # Multiple calls to digest - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - cipher.update(self.data) - first_mac = cipher.digest() - for x in range(4): - self.assertEqual(first_mac, cipher.digest()) - - # Multiple calls to verify - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - cipher.update(self.data) - for x in range(5): - cipher.verify(first_mac) - - def test_valid_encrypt_and_digest_decrypt_and_verify(self): - # encrypt_and_digest - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - cipher.update(self.data) - ct, mac = cipher.encrypt_and_digest(self.data) - - # decrypt_and_verify - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - cipher.update(self.data) - pt = cipher.decrypt_and_verify(ct, mac) - self.assertEqual(self.data, pt) - - def test_invalid_multiple_encrypt_and_digest(self): - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - ct, tag = cipher.encrypt_and_digest(self.data) - self.assertRaises(TypeError, cipher.encrypt_and_digest, b'') - - def test_invalid_multiple_decrypt_and_verify(self): - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - ct, tag = cipher.encrypt_and_digest(self.data) - - cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) - cipher.decrypt_and_verify(ct, tag) - self.assertRaises(TypeError, cipher.decrypt_and_verify, ct, tag) - - -def transform(tv): - new_tv = [[unhexlify(x) for x in tv[0].split("-")]] - new_tv += [ unhexlify(x) for x in tv[1:5]] - if tv[5]: - nonce = unhexlify(tv[5]) - else: - nonce = None - new_tv += [ nonce ] - return new_tv - - -class TestVectors(unittest.TestCase): - """Class exercising the SIV test vectors found in RFC5297""" - - # This is a list of tuples with 5 items: - # - # 1. Header + '|' + plaintext - # 2. Header + '|' + ciphertext + '|' + MAC - # 3. AES-128 key - # 4. Description - # 5. Dictionary of parameters to be passed to AES.new(). - # It must include the nonce. - # - # A "Header" is a dash ('-') separated sequece of components. - # - test_vectors_hex = [ - ( - '101112131415161718191a1b1c1d1e1f2021222324252627', - '112233445566778899aabbccddee', - '40c02b9690c4dc04daef7f6afe5c', - '85632d07c6e8f37f950acd320a2ecc93', - 'fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff', - None - ), - ( - '00112233445566778899aabbccddeeffdeaddadadeaddadaffeeddccbbaa9988' + - '7766554433221100-102030405060708090a0', - '7468697320697320736f6d6520706c61696e7465787420746f20656e63727970' + - '74207573696e67205349562d414553', - 'cb900f2fddbe404326601965c889bf17dba77ceb094fa663b7a3f748ba8af829' + - 'ea64ad544a272e9c485b62a3fd5c0d', - '7bdb6e3b432667eb06f4d14bff2fbd0f', - '7f7e7d7c7b7a79787776757473727170404142434445464748494a4b4c4d4e4f', - '09f911029d74e35bd84156c5635688c0' - ), - ] - - test_vectors = [ transform(tv) for tv in test_vectors_hex ] - - def runTest(self): - for assoc_data, pt, ct, mac, key, nonce in self.test_vectors: - - # Encrypt - cipher = AES.new(key, AES.MODE_SIV, nonce=nonce) - for x in assoc_data: - cipher.update(x) - ct2, mac2 = cipher.encrypt_and_digest(pt) - self.assertEqual(ct, ct2) - self.assertEqual(mac, mac2) - - # Decrypt - cipher = AES.new(key, AES.MODE_SIV, nonce=nonce) - for x in assoc_data: - cipher.update(x) - pt2 = cipher.decrypt_and_verify(ct, mac) - self.assertEqual(pt, pt2) - - -class TestVectorsWycheproof(unittest.TestCase): - - def __init__(self): - unittest.TestCase.__init__(self) - self._id = "None" - - def setUp(self): - self.tv = load_test_vectors_wycheproof(("Cipher", "wycheproof"), - "aes_siv_cmac_test.json", - "Wycheproof AES SIV") - - def shortDescription(self): - return self._id - - def test_encrypt(self, tv): - self._id = "Wycheproof Encrypt AES-SIV Test #" + str(tv.id) - - cipher = AES.new(tv.key, AES.MODE_SIV) - cipher.update(tv.aad) - ct, tag = cipher.encrypt_and_digest(tv.msg) - if tv.valid: - self.assertEqual(tag + ct, tv.ct) - - def test_decrypt(self, tv): - self._id = "Wycheproof Decrypt AES_SIV Test #" + str(tv.id) - - cipher = AES.new(tv.key, AES.MODE_SIV) - cipher.update(tv.aad) - try: - pt = cipher.decrypt_and_verify(tv.ct[16:], tv.ct[:16]) - except ValueError: - assert not tv.valid - else: - assert tv.valid - self.assertEqual(pt, tv.msg) - - def runTest(self): - - for tv in self.tv: - self.test_encrypt(tv) - self.test_decrypt(tv) - - -class TestVectorsWycheproof2(unittest.TestCase): - - def __init__(self): - unittest.TestCase.__init__(self) - self._id = "None" - - def setUp(self): - self.tv = load_test_vectors_wycheproof(("Cipher", "wycheproof"), - "aead_aes_siv_cmac_test.json", - "Wycheproof AEAD SIV") - - def shortDescription(self): - return self._id - - def test_encrypt(self, tv): - self._id = "Wycheproof Encrypt AEAD-AES-SIV Test #" + str(tv.id) - - cipher = AES.new(tv.key, AES.MODE_SIV, nonce=tv.iv) - cipher.update(tv.aad) - ct, tag = cipher.encrypt_and_digest(tv.msg) - if tv.valid: - self.assertEqual(ct, tv.ct) - self.assertEqual(tag, tv.tag) - - def test_decrypt(self, tv): - self._id = "Wycheproof Decrypt AEAD-AES-SIV Test #" + str(tv.id) - - cipher = AES.new(tv.key, AES.MODE_SIV, nonce=tv.iv) - cipher.update(tv.aad) - try: - pt = cipher.decrypt_and_verify(tv.ct, tv.tag) - except ValueError: - assert not tv.valid - else: - assert tv.valid - self.assertEqual(pt, tv.msg) - - def runTest(self): - - for tv in self.tv: - self.test_encrypt(tv) - self.test_decrypt(tv) - - -def get_tests(config={}): - wycheproof_warnings = config.get('wycheproof_warnings') - - tests = [] - tests += list_test_cases(SivTests) - tests += list_test_cases(SivFSMTests) - tests += [ TestVectors() ] - tests += [ TestVectorsWycheproof() ] - tests += [ TestVectorsWycheproof2() ] - return tests - - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_Salsa20.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_Salsa20.py deleted file mode 100644 index a444906..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_Salsa20.py +++ /dev/null @@ -1,367 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Cipher/Salsa20.py: Self-test for the Salsa20 stream cipher -# -# Written in 2013 by Fabrizio Tarizzo -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Cipher.Salsa20""" - -import unittest - -from Cryptodome.Util.py3compat import bchr - -from Cryptodome.SelfTest.st_common import list_test_cases - -from Cryptodome.Cipher import Salsa20 - -from .common import make_stream_tests - -# This is a list of (plaintext, ciphertext, key[, description[, params]]) -# tuples. -test_data = [ - # Test vectors are taken from - # http://www.ecrypt.eu.org/stream/svn/viewcvs.cgi/ecrypt/trunk/submissions/salsa20/full/verified.test-vectors - ( '00' * 512, - '4dfa5e481da23ea09a31022050859936da52fcee218005164f267cb65f5cfd7f' - + '2b4f97e0ff16924a52df269515110a07f9e460bc65ef95da58f740b7d1dbb0aa' - + 'd64cec189c7eb8c6bbf3d7376c80a481d43e628701f6a27afb9fe23919f24114' - + '8db44f70d7063efcc3dd55a0893a613c3c6fe1c127bd6f59910589293bb6ef9e' - + 'e24819066dee1a64f49b0bbad5988635272b169af861f85df881939f29ada6fd' - + '0241410e8d332ae4798d929434a2630de451ec4e0169694cbaa7ebb121ea6a2b' - + 'da9c1581f429e0a00f7d67e23b730676783b262e8eb43a25f55fb90b3e753aef' - + '8c6713ec66c51881111593ccb3e8cb8f8de124080501eeeb389c4bcb6977cf95' - + '7d5789631eb4554400e1e025935dfa7b3e9039d61bdc58a8697d36815bf1985c' - + 'efdf7ae112e5bb81e37ecf0616ce7147fc08a93a367e08631f23c03b00a8da2f' - + 'aa5024e5c8d30aca43fc2d5082067b21b234bc741d68fb292c6012c3764ccee3' - + '1e364a5403e00cfee338a21a01e7d3cefd5a770ca0ab48c435ea6116435f7ad8' - + '30b217b49f978a68e207ed9f462af7fb195b2115fe8f24f152e4ddc32202d6f2' - + 'b52fafbcfbc202d8a259a611e901d3f62d065eb13f09bbc45cd45119b843efaa' - + 'b375703739daced4dd4059fd71c3c47fc2f9939670fad4a46066adcc6a564578' - + '3308b90ffb72be04a6b147cbe38cc0c3b9267c296a92a7c69873f9f263be9703', - '80000000000000000000000000000000', - '128 bits key, set 1, vector 0', - dict (iv='00'*8)), - - ( '00' * 512, - 'e3be8fdd8beca2e3ea8ef9475b29a6e7003951e1097a5c38d23b7a5fad9f6844' - + 'b22c97559e2723c7cbbd3fe4fc8d9a0744652a83e72a9c461876af4d7ef1a117' - + '8da2b74eef1b6283e7e20166abcae538e9716e4669e2816b6b20c5c356802001' - + 'cc1403a9a117d12a2669f456366d6ebb0f1246f1265150f793cdb4b253e348ae' - + '203d89bc025e802a7e0e00621d70aa36b7e07cb1e7d5b38d5e222b8b0e4b8407' - + '0142b1e29504767d76824850320b5368129fdd74e861b498e3be8d16f2d7d169' - + '57be81f47b17d9ae7c4ff15429a73e10acf250ed3a90a93c711308a74c6216a9' - + 'ed84cd126da7f28e8abf8bb63517e1ca98e712f4fb2e1a6aed9fdc73291faa17' - + '958211c4ba2ebd5838c635edb81f513a91a294e194f1c039aeec657dce40aa7e' - + '7c0af57cacefa40c9f14b71a4b3456a63e162ec7d8d10b8ffb1810d71001b618' - + '2f9f73da53b85405c11f7b2d890fa8ae0c7f2e926d8a98c7ec4e91b65120e988' - + '349631a700c6facec3471cb0413656e75e309456584084d7e12c5b43a41c43ed' - + '9a048abd9b880da65f6a665a20fe7b77cd292fe62cae644b7f7df69f32bdb331' - + '903e6505ce44fdc293920c6a9ec7057e23df7dad298f82ddf4efb7fdc7bfc622' - + '696afcfd0cddcc83c7e77f11a649d79acdc3354e9635ff137e929933a0bd6f53' - + '77efa105a3a4266b7c0d089d08f1e855cc32b15b93784a36e56a76cc64bc8477', - '8000000000000000000000000000000000000000000000000000000000000000', - '256 bits key, set 1, vector 0', - dict (iv='00'*8)), - - ( '00' * 512, - '169060ccb42bea7bee4d8012a02f3635eb7bca12859fa159cd559094b3507db8' - + '01735d1a1300102a9c9415546829cbd2021ba217b39b81d89c55b13d0c603359' - + '3f84159a3c84f4b4f4a0edcd9d38ff261a737909e0b66d68b5cac496f3a5be99' - + 'cb12c321ab711afaab36cc0947955e1a9bb952ed54425e7711279fbc81bb83f5' - + '6e55cea44e6daddb05858a153ea6213b3350c12aa1a83ef2726f09485fa71790' - + 'f9b9f922c7dda1113b1f9d56658ed3402803f511bc1f122601d5e7f0ff036e23' - + '23ef24bb24195b9fd574823cd8a40c29d86bd35c191e2038779ff696c712b6d8' - + '2e7014dbe1ac5d527af076c088c4a8d44317958189f6ef54933a7e0816b5b916' - + 'd8f12ed8afe9422b85e5cc9b8adec9d6cfabe8dbc1082bccc02f5a7266aa074c' - + 'a284e583a35837798cc0e69d4ce937653b8cdd65ce414b89138615ccb165ad19' - + '3c6b9c3d05eef4be921a10ea811fe61d11c6867600188e065daff90b509ec56b' - + 'd41e7e8968c478c78d590c2d2ee24ea009c8f49bc3d81672cfc47895a9e21c9a' - + '471ebf8e294bee5d2de436ac8d052bf31111b345f1da23c3a4d13b9fc5f0900a' - + 'a298f98f538973b8fad40d4d159777de2cfe2a3dead1645ddb49794827dba040' - + 'f70a0ff4ecd155e0f033604693a51e2363880e2ecf98699e7174af7c2c6b0fc6' - + '59ae329599a3949272a37b9b2183a0910922a3f325ae124dcbdd735364055ceb', - '09090909090909090909090909090909', - '128 bits key, set 2, vector 9', - dict (iv='00'*8)), - - ( '00' * 512, - '7041e747ceb22ed7812985465f50333124f971da1c5d6efe5ca201b886f31046' - + 'e757e5c3ec914f60ed1f6bce2819b6810953f12b8ba1199bf82d746a8b8a88f1' - + '142002978ec4c35b95dc2c82990f9e847a0ab45f2ca72625f5190c820f29f3aa' - + 'f5f0b5572b06b70a144f2a240c3b3098d4831fa1ce1459f8d1df226a6a79b0ab' - + '41e91799ef31b5ff3d756c19126b19025858ee70fbd69f2be955cb011c005e31' - + '32b271b378f39b0cb594e95c99ce6ff17735a541891845bbf0450afcb4a850b9' - + '4ee90afb713ae7e01295c74381180a3816d7020d5a396c0d97aaa783eaabb6ec' - + '44d5111157f2212d1b1b8fca7893e8b520cd482418c272ab119b569a2b9598eb' - + '355624d12e79adab81153b58cd22eaf1b2a32395dedc4a1c66f4d274070b9800' - + 'ea95766f0245a8295f8aadb36ddbbdfa936417c8dbc6235d19494036964d3e70' - + 'b125b0f800c3d53881d9d11e7970f827c2f9556935cd29e927b0aceb8cae5fd4' - + '0fd88a8854010a33db94c96c98735858f1c5df6844f864feaca8f41539313e7f' - + '3c0610214912cd5e6362197646207e2d64cd5b26c9dfe0822629dcbeb16662e8' - + '9ff5bf5cf2e499138a5e27bd5027329d0e68ddf53103e9e409523662e27f61f6' - + '5cf38c1232023e6a6ef66c315bcb2a4328642faabb7ca1e889e039e7c444b34b' - + 'b3443f596ac730f3df3dfcdb343c307c80f76e43e8898c5e8f43dc3bb280add0', - '0909090909090909090909090909090909090909090909090909090909090909', - '256 bits key, set 2, vector 9', - dict (iv='00'*8)), - - ( '00' * 1024, - '71daee5142d0728b41b6597933ebf467e43279e30978677078941602629cbf68' - + 'b73d6bd2c95f118d2b3e6ec955dabb6dc61c4143bc9a9b32b99dbe6866166dc0' - + '8631b7d6553050303d7252c264d3a90d26c853634813e09ad7545a6ce7e84a5d' - + 'fc75ec43431207d5319970b0faadb0e1510625bb54372c8515e28e2accf0a993' - + '0ad15f431874923d2a59e20d9f2a5367dba6051564f150287debb1db536ff9b0' - + '9ad981f25e5010d85d76ee0c305f755b25e6f09341e0812f95c94f42eead346e' - + '81f39c58c5faa2c88953dc0cac90469db2063cb5cdb22c9eae22afbf0506fca4' - + '1dc710b846fbdfe3c46883dd118f3a5e8b11b6afd9e71680d8666557301a2daa' - + 'fb9496c559784d35a035360885f9b17bd7191977deea932b981ebdb29057ae3c' - + '92cfeff5e6c5d0cb62f209ce342d4e35c69646ccd14e53350e488bb310a32f8b' - + '0248e70acc5b473df537ced3f81a014d4083932bedd62ed0e447b6766cd2604b' - + '706e9b346c4468beb46a34ecf1610ebd38331d52bf33346afec15eefb2a7699e' - + '8759db5a1f636a48a039688e39de34d995df9f27ed9edc8dd795e39e53d9d925' - + 'b278010565ff665269042f05096d94da3433d957ec13d2fd82a0066283d0d1ee' - + 'b81bf0ef133b7fd90248b8ffb499b2414cd4fa003093ff0864575a43749bf596' - + '02f26c717fa96b1d057697db08ebc3fa664a016a67dcef8807577cc3a09385d3' - + 'f4dc79b34364bb3b166ce65fe1dd28e3950fe6fa81063f7b16ce1c0e6daac1f8' - + '188455b77752045e863c9b256ad92bc6e2d08314c5bba191c274f42dfbb3d652' - + 'bb771956555e880f84cd8b827a4c5a52f3a099fa0259bd4aac3efd541f191170' - + '4412d6e85fbcc628b335875b9fef24807f6e1bc66c3186159e1e7f5a13913e02' - + 'd241ce2efdbcaa275039fb14eac5923d17ffbc7f1abd3b45e92127575bfbabf9' - + '3a257ebef0aa1437b326e41b585af572f7239c33b32981a1577a4f629b027e1e' - + 'b49d58cc497e944d79cef44357c2bf25442ab779651e991147bf79d6fd3a8868' - + '0cd3b1748e07fd10d78aceef6db8a5e563570d40127f754146c34a440f2a991a' - + '23fa39d365141f255041f2135c5cba4373452c114da1801bacca38610e3a6524' - + '2b822d32de4ab5a7d3cf9b61b37493c863bd12e2cae10530cddcda2cb7a5436b' - + 'ef8988d4d24e8cdc31b2d2a3586340bc5141f8f6632d0dd543bfed81eb471ba1' - + 'f3dc2225a15ffddcc03eb48f44e27e2aa390598adf83f15c6608a5f18d4dfcf0' - + 'f547d467a4d70b281c83a595d7660d0b62de78b9cca023cca89d7b1f83484638' - + '0e228c25f049184a612ef5bb3d37454e6cfa5b10dceda619d898a699b3c8981a' - + '173407844bb89b4287bf57dd6600c79e352c681d74b03fa7ea0d7bf6ad69f8a6' - + '8ecb001963bd2dd8a2baa0083ec09751cd9742402ad716be16d5c052304cfca1', - '0F62B5085BAE0154A7FA4DA0F34699EC', - '128 bits key, Set 6, vector# 3', - dict (iv='288FF65DC42B92F9')), - - ( '00' * 1024, - '5e5e71f90199340304abb22a37b6625bf883fb89ce3b21f54a10b81066ef87da' - + '30b77699aa7379da595c77dd59542da208e5954f89e40eb7aa80a84a6176663f' - + 'd910cde567cf1ff60f7040548d8f376bfd1f44c4774aac37410ede7d5c3463fc' - + '4508a603201d8495ad257894e5eb1914b53e8da5e4bf2bc83ac87ce55cc67df7' - + '093d9853d2a83a9c8be969175df7c807a17156df768445dd0874a9271c6537f5' - + 'ce0466473582375f067fa4fcdaf65dbc0139cd75e8c21a482f28c0fb8c3d9f94' - + '22606cc8e88fe28fe73ec3cb10ff0e8cc5f2a49e540f007265c65b7130bfdb98' - + '795b1df9522da46e48b30e55d9f0d787955ece720205b29c85f3ad9be33b4459' - + '7d21b54d06c9a60b04b8e640c64e566e51566730e86cf128ab14174f91bd8981' - + 'a6fb00fe587bbd6c38b5a1dfdb04ea7e61536fd229f957aa9b070ca931358e85' - + '11b92c53c523cb54828fb1513c5636fa9a0645b4a3c922c0db94986d92f314ff' - + '7852c03b231e4dceea5dd8cced621869cff818daf3c270ff3c8be2e5c74be767' - + 'a4e1fdf3327a934fe31e46df5a74ae2021cee021d958c4f615263d99a5ddae7f' - + 'eab45e6eccbafefe4761c57750847b7e75ee2e2f14333c0779ce4678f47b1e1b' - + '760a03a5f17d6e91d4b42313b3f1077ee270e432fe04917ed1fc8babebf7c941' - + '42b80dfb44a28a2a3e59093027606f6860bfb8c2e5897078cfccda7314c70035' - + 'f137de6f05daa035891d5f6f76e1df0fce1112a2ff0ac2bd3534b5d1bf4c7165' - + 'fb40a1b6eacb7f295711c4907ae457514a7010f3a342b4427593d61ba993bc59' - + '8bd09c56b9ee53aac5dd861fa4b4bb53888952a4aa9d8ca8671582de716270e1' - + '97375b3ee49e51fa2bf4ef32015dd9a764d966aa2ae541592d0aa650849e99ca' - + '5c6c39beebf516457cc32fe4c105bff314a12f1ec94bdf4d626f5d9b1cbbde42' - + 'e5733f0885765ba29e2e82c829d312f5fc7e180679ac84826c08d0a644b326d0' - + '44da0fdcc75fa53cfe4ced0437fa4df5a7ecbca8b4cb7c4a9ecf9a60d00a56eb' - + '81da52adc21f508dbb60a9503a3cc94a896616d86020d5b0e5c637329b6d396a' - + '41a21ba2c4a9493cf33fa2d4f10f77d5b12fdad7e478ccfe79b74851fc96a7ca' - + '6320c5efd561a222c0ab0fb44bbda0e42149611d2262bb7d1719150fa798718a' - + '0eec63ee297cad459869c8b0f06c4e2b56cbac03cd2605b2a924efedf85ec8f1' - + '9b0b6c90e7cbd933223ffeb1b3a3f9677657905829294c4c70acdb8b0891b47d' - + '0875d0cd6c0f4efe2917fc44b581ef0d1e4280197065d07da34ab33283364552' - + 'efad0bd9257b059acdd0a6f246812feb69e7e76065f27dbc2eee94da9cc41835' - + 'bf826e36e5cebe5d4d6a37a6a666246290ce51a0c082718ab0ec855668db1add' - + 'a658e5f257e0db39384d02e6145c4c00eaa079098f6d820d872de711b6ed08cf', - '0F62B5085BAE0154A7FA4DA0F34699EC3F92E5388BDE3184D72A7DD02376C91C', - '256 bits key, Set 6, vector# 3', - dict (iv='288FF65DC42B92F9')), - -] - - -class KeyLength(unittest.TestCase): - - def runTest(self): - - nonce = bchr(0) * 8 - for key_length in (15, 30, 33): - key = bchr(1) * key_length - self.assertRaises(ValueError, Salsa20.new, key, nonce) - - -class NonceTests(unittest.TestCase): - - def test_invalid_nonce_length(self): - key = bchr(1) * 16 - self.assertRaises(ValueError, Salsa20.new, key, bchr(0) * 7) - self.assertRaises(ValueError, Salsa20.new, key, bchr(0) * 9) - - def test_default_nonce(self): - - cipher1 = Salsa20.new(bchr(1) * 16) - cipher2 = Salsa20.new(bchr(1) * 16) - self.assertEqual(len(cipher1.nonce), 8) - self.assertNotEqual(cipher1.nonce, cipher2.nonce) - - -class ByteArrayTest(unittest.TestCase): - """Verify we can encrypt or decrypt bytearrays""" - - def runTest(self): - - data = b"0123" - key = b"9" * 32 - nonce = b"t" * 8 - - # Encryption - data_ba = bytearray(data) - key_ba = bytearray(key) - nonce_ba = bytearray(nonce) - - cipher1 = Salsa20.new(key=key, nonce=nonce) - ct = cipher1.encrypt(data) - - cipher2 = Salsa20.new(key=key_ba, nonce=nonce_ba) - key_ba[:1] = b'\xFF' - nonce_ba[:1] = b'\xFF' - ct_test = cipher2.encrypt(data_ba) - - self.assertEqual(ct, ct_test) - self.assertEqual(cipher1.nonce, cipher2.nonce) - - # Decryption - key_ba = bytearray(key) - nonce_ba = bytearray(nonce) - ct_ba = bytearray(ct) - - cipher3 = Salsa20.new(key=key_ba, nonce=nonce_ba) - key_ba[:1] = b'\xFF' - nonce_ba[:1] = b'\xFF' - pt_test = cipher3.decrypt(ct_ba) - - self.assertEqual(data, pt_test) - - -class MemoryviewTest(unittest.TestCase): - """Verify we can encrypt or decrypt bytearrays""" - - def runTest(self): - - data = b"0123" - key = b"9" * 32 - nonce = b"t" * 8 - - # Encryption - data_mv = memoryview(bytearray(data)) - key_mv = memoryview(bytearray(key)) - nonce_mv = memoryview(bytearray(nonce)) - - cipher1 = Salsa20.new(key=key, nonce=nonce) - ct = cipher1.encrypt(data) - - cipher2 = Salsa20.new(key=key_mv, nonce=nonce_mv) - key_mv[:1] = b'\xFF' - nonce_mv[:1] = b'\xFF' - ct_test = cipher2.encrypt(data_mv) - - self.assertEqual(ct, ct_test) - self.assertEqual(cipher1.nonce, cipher2.nonce) - - # Decryption - key_mv = memoryview(bytearray(key)) - nonce_mv = memoryview(bytearray(nonce)) - ct_mv = memoryview(bytearray(ct)) - - cipher3 = Salsa20.new(key=key_mv, nonce=nonce_mv) - key_mv[:1] = b'\xFF' - nonce_mv[:1] = b'\xFF' - pt_test = cipher3.decrypt(ct_mv) - - self.assertEqual(data, pt_test) - - -class TestOutput(unittest.TestCase): - - def runTest(self): - # Encrypt/Decrypt data and test output parameter - - key = b'4' * 32 - nonce = b'5' * 8 - cipher = Salsa20.new(key=key, nonce=nonce) - - pt = b'5' * 300 - ct = cipher.encrypt(pt) - - output = bytearray(len(pt)) - cipher = Salsa20.new(key=key, nonce=nonce) - res = cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - self.assertEqual(res, None) - - cipher = Salsa20.new(key=key, nonce=nonce) - res = cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - self.assertEqual(res, None) - - output = memoryview(bytearray(len(pt))) - cipher = Salsa20.new(key=key, nonce=nonce) - cipher.encrypt(pt, output=output) - self.assertEqual(ct, output) - - cipher = Salsa20.new(key=key, nonce=nonce) - cipher.decrypt(ct, output=output) - self.assertEqual(pt, output) - - cipher = Salsa20.new(key=key, nonce=nonce) - self.assertRaises(TypeError, cipher.encrypt, pt, output=b'0'*len(pt)) - - cipher = Salsa20.new(key=key, nonce=nonce) - self.assertRaises(TypeError, cipher.decrypt, ct, output=b'0'*len(ct)) - - shorter_output = bytearray(len(pt) - 1) - - cipher = Salsa20.new(key=key, nonce=nonce) - self.assertRaises(ValueError, cipher.encrypt, pt, output=shorter_output) - - cipher = Salsa20.new(key=key, nonce=nonce) - self.assertRaises(ValueError, cipher.decrypt, ct, output=shorter_output) - - -def get_tests(config={}): - tests = make_stream_tests(Salsa20, "Salsa20", test_data) - tests.append(KeyLength()) - tests += list_test_cases(NonceTests) - tests.append(ByteArrayTest()) - tests.append(MemoryviewTest()) - tests.append(TestOutput()) - - return tests - - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_pkcs1_15.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_pkcs1_15.py deleted file mode 100644 index 12c09dd..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_pkcs1_15.py +++ /dev/null @@ -1,283 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Cipher/test_pkcs1_15.py: Self-test for PKCS#1 v1.5 encryption -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -from __future__ import print_function - -import unittest - -from Cryptodome.PublicKey import RSA -from Cryptodome.SelfTest.st_common import list_test_cases, a2b_hex -from Cryptodome import Random -from Cryptodome.Cipher import PKCS1_v1_5 as PKCS -from Cryptodome.Util.py3compat import b -from Cryptodome.Util.number import bytes_to_long, long_to_bytes -from Cryptodome.SelfTest.loader import load_test_vectors_wycheproof - - -def rws(t): - """Remove white spaces, tabs, and new lines from a string""" - for c in ['\n', '\t', ' ']: - t = t.replace(c, '') - return t - - -def t2b(t): - """Convert a text string with bytes in hex form to a byte string""" - clean = b(rws(t)) - if len(clean) % 2 == 1: - raise ValueError("Even number of characters expected") - return a2b_hex(clean) - - -class PKCS1_15_Tests(unittest.TestCase): - - def setUp(self): - self.rng = Random.new().read - self.key1024 = RSA.generate(1024, self.rng) - - # List of tuples with test data for PKCS#1 v1.5. - # Each tuple is made up by: - # Item #0: dictionary with RSA key component, or key to import - # Item #1: plaintext - # Item #2: ciphertext - # Item #3: random data - - _testData = ( - - # - # Generated with openssl 0.9.8o - # - ( - # Private key - '''-----BEGIN RSA PRIVATE KEY----- -MIICXAIBAAKBgQDAiAnvIAOvqVwJTaYzsKnefZftgtXGE2hPJppGsWl78yz9jeXY -W/FxX/gTPURArNhdnhP6n3p2ZaDIBrO2zizbgIXs0IsljTTcr4vnI8fMXzyNUOjA -zP3nzMqZDZK6757XQAobOssMkBFqRWwilT/3DsBhRpl3iMUhF+wvpTSHewIDAQAB -AoGAC4HV/inOrpgTvSab8Wj0riyZgQOZ3U3ZpSlsfR8ra9Ib9Uee3jCYnKscu6Gk -y6zI/cdt8EPJ4PuwAWSNJzbpbVaDvUq25OD+CX8/uRT08yBS4J8TzBitZJTD4lS7 -atdTnKT0Wmwk+u8tDbhvMKwnUHdJLcuIsycts9rwJVapUtkCQQDvDpx2JMun0YKG -uUttjmL8oJ3U0m3ZvMdVwBecA0eebZb1l2J5PvI3EJD97eKe91Nsw8T3lwpoN40k -IocSVDklAkEAzi1HLHE6EzVPOe5+Y0kGvrIYRRhncOb72vCvBZvD6wLZpQgqo6c4 -d3XHFBBQWA6xcvQb5w+VVEJZzw64y25sHwJBAMYReRl6SzL0qA0wIYrYWrOt8JeQ -8mthulcWHXmqTgC6FEXP9Es5GD7/fuKl4wqLKZgIbH4nqvvGay7xXLCXD/ECQH9a -1JYNMtRen5unSAbIOxRcKkWz92F0LKpm9ZW/S9vFHO+mBcClMGoKJHiuQxLBsLbT -NtEZfSJZAeS2sUtn3/0CQDb2M2zNBTF8LlM0nxmh0k9VGm5TVIyBEMcipmvOgqIs -HKukWBcq9f/UOmS0oEhai/6g+Uf7VHJdWaeO5LzuvwU= ------END RSA PRIVATE KEY-----''', - # Plaintext - '''THIS IS PLAINTEXT\x0A''', - # Ciphertext - '''3f dc fd 3c cd 5c 9b 12 af 65 32 e3 f7 d0 da 36 - 8f 8f d9 e3 13 1c 7f c8 b3 f9 c1 08 e4 eb 79 9c - 91 89 1f 96 3b 94 77 61 99 a4 b1 ee 5d e6 17 c9 - 5d 0a b5 63 52 0a eb 00 45 38 2a fb b0 71 3d 11 - f7 a1 9e a7 69 b3 af 61 c0 bb 04 5b 5d 4b 27 44 - 1f 5b 97 89 ba 6a 08 95 ee 4f a2 eb 56 64 e5 0f - da 7c f9 9a 61 61 06 62 ed a0 bc 5f aa 6c 31 78 - 70 28 1a bb 98 3c e3 6a 60 3c d1 0b 0f 5a f4 75''', - # Random data - '''eb d7 7d 86 a4 35 23 a3 54 7e 02 0b 42 1d - 61 6c af 67 b8 4e 17 56 80 66 36 04 64 34 26 8a - 47 dd 44 b3 1a b2 17 60 f4 91 2e e2 b5 95 64 cc - f9 da c8 70 94 54 86 4c ef 5b 08 7d 18 c4 ab 8d - 04 06 33 8f ca 15 5f 52 60 8a a1 0c f5 08 b5 4c - bb 99 b8 94 25 04 9c e6 01 75 e6 f9 63 7a 65 61 - 13 8a a7 47 77 81 ae 0d b8 2c 4d 50 a5''' - ), - ) - - def testEncrypt1(self): - for test in self._testData: - # Build the key - key = RSA.importKey(test[0]) - # RNG that takes its random numbers from a pool given - # at initialization - class randGen: - def __init__(self, data): - self.data = data - self.idx = 0 - def __call__(self, N): - r = self.data[self.idx:self.idx+N] - self.idx += N - return r - # The real test - cipher = PKCS.new(key, randfunc=randGen(t2b(test[3]))) - ct = cipher.encrypt(b(test[1])) - self.assertEqual(ct, t2b(test[2])) - - def testEncrypt2(self): - # Verify that encryption fail if plaintext is too long - pt = '\x00'*(128-11+1) - cipher = PKCS.new(self.key1024) - self.assertRaises(ValueError, cipher.encrypt, pt) - - def testVerify1(self): - for test in self._testData: - key = RSA.importKey(test[0]) - expected_pt = b(test[1]) - ct = t2b(test[2]) - cipher = PKCS.new(key) - - # The real test - pt = cipher.decrypt(ct, None) - self.assertEqual(pt, expected_pt) - - pt = cipher.decrypt(ct, b'\xFF' * len(expected_pt)) - self.assertEqual(pt, expected_pt) - - def testVerify2(self): - # Verify that decryption fails if ciphertext is not as long as - # RSA modulus - cipher = PKCS.new(self.key1024) - self.assertRaises(ValueError, cipher.decrypt, '\x00'*127, "---") - self.assertRaises(ValueError, cipher.decrypt, '\x00'*129, "---") - - # Verify that decryption fails if there are less then 8 non-zero padding - # bytes - pt = b('\x00\x02' + '\xFF'*7 + '\x00' + '\x45'*118) - pt_int = bytes_to_long(pt) - ct_int = self.key1024._encrypt(pt_int) - ct = long_to_bytes(ct_int, 128) - self.assertEqual(b"---", cipher.decrypt(ct, b"---")) - - def testEncryptVerify1(self): - # Encrypt/Verify messages of length [0..RSAlen-11] - # and therefore padding [8..117] - for pt_len in range(0, 128 - 11 + 1): - pt = self.rng(pt_len) - cipher = PKCS.new(self.key1024) - ct = cipher.encrypt(pt) - pt2 = cipher.decrypt(ct, b'\xAA' * pt_len) - self.assertEqual(pt, pt2) - - def test_encrypt_verify_exp_pt_len(self): - - cipher = PKCS.new(self.key1024) - pt = b'5' * 16 - ct = cipher.encrypt(pt) - sentinel = b'\xAA' * 16 - - pt_A = cipher.decrypt(ct, sentinel, 16) - self.assertEqual(pt, pt_A) - - pt_B = cipher.decrypt(ct, sentinel, 15) - self.assertEqual(sentinel, pt_B) - - pt_C = cipher.decrypt(ct, sentinel, 17) - self.assertEqual(sentinel, pt_C) - - def testByteArray(self): - pt = b"XER" - cipher = PKCS.new(self.key1024) - ct = cipher.encrypt(bytearray(pt)) - pt2 = cipher.decrypt(bytearray(ct), '\xFF' * len(pt)) - self.assertEqual(pt, pt2) - - def testMemoryview(self): - pt = b"XER" - cipher = PKCS.new(self.key1024) - ct = cipher.encrypt(memoryview(bytearray(pt))) - pt2 = cipher.decrypt(memoryview(bytearray(ct)), b'\xFF' * len(pt)) - self.assertEqual(pt, pt2) - - def test_return_type(self): - pt = b"XYZ" - cipher = PKCS.new(self.key1024) - ct = cipher.encrypt(pt) - self.assertTrue(isinstance(ct, bytes)) - pt2 = cipher.decrypt(ct, b'\xAA' * 3) - self.assertTrue(isinstance(pt2, bytes)) - - -class TestVectorsWycheproof(unittest.TestCase): - - def __init__(self, wycheproof_warnings, skip_slow_tests): - unittest.TestCase.__init__(self) - self._wycheproof_warnings = wycheproof_warnings - self._skip_slow_tests = skip_slow_tests - self._id = "None" - - def load_tests(self, filename): - - def filter_rsa(group): - return RSA.import_key(group['privateKeyPem']) - - result = load_test_vectors_wycheproof(("Cipher", "wycheproof"), - filename, - "Wycheproof PKCS#1v1.5 (%s)" % filename, - group_tag={'rsa_key': filter_rsa} - ) - return result - - def setUp(self): - self.tv = [] - self.tv.extend(self.load_tests("rsa_pkcs1_2048_test.json")) - if not self._skip_slow_tests: - self.tv.extend(self.load_tests("rsa_pkcs1_3072_test.json")) - self.tv.extend(self.load_tests("rsa_pkcs1_4096_test.json")) - - def shortDescription(self): - return self._id - - def warn(self, tv): - if tv.warning and self._wycheproof_warnings: - import warnings - warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment)) - - def test_decrypt(self, tv): - self._id = "Wycheproof Decrypt PKCS#1v1.5 Test #%s" % tv.id - sentinel = b'\xAA' * max(3, len(tv.msg)) - cipher = PKCS.new(tv.rsa_key) - try: - pt = cipher.decrypt(tv.ct, sentinel=sentinel) - except ValueError: - assert not tv.valid - else: - if pt == sentinel: - assert not tv.valid - else: - assert tv.valid - self.assertEqual(pt, tv.msg) - self.warn(tv) - - def runTest(self): - - for tv in self.tv: - self.test_decrypt(tv) - - -def get_tests(config={}): - skip_slow_tests = not config.get('slow_tests') - wycheproof_warnings = config.get('wycheproof_warnings') - - tests = [] - tests += list_test_cases(PKCS1_15_Tests) - tests += [TestVectorsWycheproof(wycheproof_warnings, skip_slow_tests)] - return tests - - -if __name__ == '__main__': - def suite(): - return unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_pkcs1_oaep.py b/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_pkcs1_oaep.py deleted file mode 100644 index aa00c9c..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Cipher/test_pkcs1_oaep.py +++ /dev/null @@ -1,506 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Cipher/test_pkcs1_oaep.py: Self-test for PKCS#1 OAEP encryption -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -import unittest - -from Cryptodome.SelfTest.st_common import list_test_cases, a2b_hex -from Cryptodome.SelfTest.loader import load_test_vectors_wycheproof - -from Cryptodome.PublicKey import RSA -from Cryptodome.Cipher import PKCS1_OAEP as PKCS -from Cryptodome.Hash import MD2, MD5, SHA1, SHA256, RIPEMD160, SHA224, SHA384, SHA512 -from Cryptodome import Random -from Cryptodome.Signature.pss import MGF1 - -from Cryptodome.Util.py3compat import b, bchr - - -def rws(t): - """Remove white spaces, tabs, and new lines from a string""" - for c in ['\n', '\t', ' ']: - t = t.replace(c, '') - return t - - -def t2b(t): - """Convert a text string with bytes in hex form to a byte string""" - clean = rws(t) - if len(clean) % 2 == 1: - raise ValueError("Even number of characters expected") - return a2b_hex(clean) - - -class PKCS1_OAEP_Tests(unittest.TestCase): - - def setUp(self): - self.rng = Random.new().read - self.key1024 = RSA.generate(1024, self.rng) - - # List of tuples with test data for PKCS#1 OAEP - # Each tuple is made up by: - # Item #0: dictionary with RSA key component - # Item #1: plaintext - # Item #2: ciphertext - # Item #3: random data (=seed) - # Item #4: hash object - - _testData = ( - - # - # From in oaep-int.txt to be found in - # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip - # - ( - # Private key - { - 'n':'''bb f8 2f 09 06 82 ce 9c 23 38 ac 2b 9d a8 71 f7 - 36 8d 07 ee d4 10 43 a4 40 d6 b6 f0 74 54 f5 1f - b8 df ba af 03 5c 02 ab 61 ea 48 ce eb 6f cd 48 - 76 ed 52 0d 60 e1 ec 46 19 71 9d 8a 5b 8b 80 7f - af b8 e0 a3 df c7 37 72 3e e6 b4 b7 d9 3a 25 84 - ee 6a 64 9d 06 09 53 74 88 34 b2 45 45 98 39 4e - e0 aa b1 2d 7b 61 a5 1f 52 7a 9a 41 f6 c1 68 7f - e2 53 72 98 ca 2a 8f 59 46 f8 e5 fd 09 1d bd cb''', - # Public key - 'e':'11', - # In the test vector, only p and q were given... - # d is computed offline as e^{-1} mod (p-1)(q-1) - 'd':'''a5dafc5341faf289c4b988db30c1cdf83f31251e0 - 668b42784813801579641b29410b3c7998d6bc465745e5c3 - 92669d6870da2c082a939e37fdcb82ec93edac97ff3ad595 - 0accfbc111c76f1a9529444e56aaf68c56c092cd38dc3bef - 5d20a939926ed4f74a13eddfbe1a1cecc4894af9428c2b7b - 8883fe4463a4bc85b1cb3c1''' - } - , - # Plaintext - '''d4 36 e9 95 69 fd 32 a7 c8 a0 5b bc 90 d3 2c 49''', - # Ciphertext - '''12 53 e0 4d c0 a5 39 7b b4 4a 7a b8 7e 9b f2 a0 - 39 a3 3d 1e 99 6f c8 2a 94 cc d3 00 74 c9 5d f7 - 63 72 20 17 06 9e 52 68 da 5d 1c 0b 4f 87 2c f6 - 53 c1 1d f8 23 14 a6 79 68 df ea e2 8d ef 04 bb - 6d 84 b1 c3 1d 65 4a 19 70 e5 78 3b d6 eb 96 a0 - 24 c2 ca 2f 4a 90 fe 9f 2e f5 c9 c1 40 e5 bb 48 - da 95 36 ad 87 00 c8 4f c9 13 0a de a7 4e 55 8d - 51 a7 4d df 85 d8 b5 0d e9 68 38 d6 06 3e 09 55''', - # Random - '''aa fd 12 f6 59 ca e6 34 89 b4 79 e5 07 6d de c2 - f0 6c b5 8f''', - # Hash - SHA1, - ), - - # - # From in oaep-vect.txt to be found in Example 1.1 - # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip - # - ( - # Private key - { - 'n':'''a8 b3 b2 84 af 8e b5 0b 38 70 34 a8 60 f1 46 c4 - 91 9f 31 87 63 cd 6c 55 98 c8 ae 48 11 a1 e0 ab - c4 c7 e0 b0 82 d6 93 a5 e7 fc ed 67 5c f4 66 85 - 12 77 2c 0c bc 64 a7 42 c6 c6 30 f5 33 c8 cc 72 - f6 2a e8 33 c4 0b f2 58 42 e9 84 bb 78 bd bf 97 - c0 10 7d 55 bd b6 62 f5 c4 e0 fa b9 84 5c b5 14 - 8e f7 39 2d d3 aa ff 93 ae 1e 6b 66 7b b3 d4 24 - 76 16 d4 f5 ba 10 d4 cf d2 26 de 88 d3 9f 16 fb''', - 'e':'''01 00 01''', - 'd':'''53 33 9c fd b7 9f c8 46 6a 65 5c 73 16 ac a8 5c - 55 fd 8f 6d d8 98 fd af 11 95 17 ef 4f 52 e8 fd - 8e 25 8d f9 3f ee 18 0f a0 e4 ab 29 69 3c d8 3b - 15 2a 55 3d 4a c4 d1 81 2b 8b 9f a5 af 0e 7f 55 - fe 73 04 df 41 57 09 26 f3 31 1f 15 c4 d6 5a 73 - 2c 48 31 16 ee 3d 3d 2d 0a f3 54 9a d9 bf 7c bf - b7 8a d8 84 f8 4d 5b eb 04 72 4d c7 36 9b 31 de - f3 7d 0c f5 39 e9 cf cd d3 de 65 37 29 ea d5 d1 ''' - } - , - # Plaintext - '''66 28 19 4e 12 07 3d b0 3b a9 4c da 9e f9 53 23 - 97 d5 0d ba 79 b9 87 00 4a fe fe 34''', - # Ciphertext - '''35 4f e6 7b 4a 12 6d 5d 35 fe 36 c7 77 79 1a 3f - 7b a1 3d ef 48 4e 2d 39 08 af f7 22 fa d4 68 fb - 21 69 6d e9 5d 0b e9 11 c2 d3 17 4f 8a fc c2 01 - 03 5f 7b 6d 8e 69 40 2d e5 45 16 18 c2 1a 53 5f - a9 d7 bf c5 b8 dd 9f c2 43 f8 cf 92 7d b3 13 22 - d6 e8 81 ea a9 1a 99 61 70 e6 57 a0 5a 26 64 26 - d9 8c 88 00 3f 84 77 c1 22 70 94 a0 d9 fa 1e 8c - 40 24 30 9c e1 ec cc b5 21 00 35 d4 7a c7 2e 8a''', - # Random - '''18 b7 76 ea 21 06 9d 69 77 6a 33 e9 6b ad 48 e1 - dd a0 a5 ef''', - SHA1 - ), - - # - # From in oaep-vect.txt to be found in Example 2.1 - # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip - # - ( - # Private key - { - 'n':'''01 94 7c 7f ce 90 42 5f 47 27 9e 70 85 1f 25 d5 - e6 23 16 fe 8a 1d f1 93 71 e3 e6 28 e2 60 54 3e - 49 01 ef 60 81 f6 8c 0b 81 41 19 0d 2a e8 da ba - 7d 12 50 ec 6d b6 36 e9 44 ec 37 22 87 7c 7c 1d - 0a 67 f1 4b 16 94 c5 f0 37 94 51 a4 3e 49 a3 2d - de 83 67 0b 73 da 91 a1 c9 9b c2 3b 43 6a 60 05 - 5c 61 0f 0b af 99 c1 a0 79 56 5b 95 a3 f1 52 66 - 32 d1 d4 da 60 f2 0e da 25 e6 53 c4 f0 02 76 6f - 45''', - 'e':'''01 00 01''', - 'd':'''08 23 f2 0f ad b5 da 89 08 8a 9d 00 89 3e 21 fa - 4a 1b 11 fb c9 3c 64 a3 be 0b aa ea 97 fb 3b 93 - c3 ff 71 37 04 c1 9c 96 3c 1d 10 7a ae 99 05 47 - 39 f7 9e 02 e1 86 de 86 f8 7a 6d de fe a6 d8 cc - d1 d3 c8 1a 47 bf a7 25 5b e2 06 01 a4 a4 b2 f0 - 8a 16 7b 5e 27 9d 71 5b 1b 45 5b dd 7e ab 24 59 - 41 d9 76 8b 9a ce fb 3c cd a5 95 2d a3 ce e7 25 - 25 b4 50 16 63 a8 ee 15 c9 e9 92 d9 24 62 fe 39''' - }, - # Plaintext - '''8f f0 0c aa 60 5c 70 28 30 63 4d 9a 6c 3d 42 c6 - 52 b5 8c f1 d9 2f ec 57 0b ee e7''', - # Ciphertext - '''01 81 af 89 22 b9 fc b4 d7 9d 92 eb e1 98 15 99 - 2f c0 c1 43 9d 8b cd 49 13 98 a0 f4 ad 3a 32 9a - 5b d9 38 55 60 db 53 26 83 c8 b7 da 04 e4 b1 2a - ed 6a ac df 47 1c 34 c9 cd a8 91 ad dc c2 df 34 - 56 65 3a a6 38 2e 9a e5 9b 54 45 52 57 eb 09 9d - 56 2b be 10 45 3f 2b 6d 13 c5 9c 02 e1 0f 1f 8a - bb 5d a0 d0 57 09 32 da cf 2d 09 01 db 72 9d 0f - ef cc 05 4e 70 96 8e a5 40 c8 1b 04 bc ae fe 72 - 0e''', - # Random - '''8c 40 7b 5e c2 89 9e 50 99 c5 3e 8c e7 93 bf 94 - e7 1b 17 82''', - SHA1 - ), - - # - # From in oaep-vect.txt to be found in Example 10.1 - # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip - # - ( - # Private key - { - 'n':'''ae 45 ed 56 01 ce c6 b8 cc 05 f8 03 93 5c 67 4d - db e0 d7 5c 4c 09 fd 79 51 fc 6b 0c ae c3 13 a8 - df 39 97 0c 51 8b ff ba 5e d6 8f 3f 0d 7f 22 a4 - 02 9d 41 3f 1a e0 7e 4e be 9e 41 77 ce 23 e7 f5 - 40 4b 56 9e 4e e1 bd cf 3c 1f b0 3e f1 13 80 2d - 4f 85 5e b9 b5 13 4b 5a 7c 80 85 ad ca e6 fa 2f - a1 41 7e c3 76 3b e1 71 b0 c6 2b 76 0e de 23 c1 - 2a d9 2b 98 08 84 c6 41 f5 a8 fa c2 6b da d4 a0 - 33 81 a2 2f e1 b7 54 88 50 94 c8 25 06 d4 01 9a - 53 5a 28 6a fe b2 71 bb 9b a5 92 de 18 dc f6 00 - c2 ae ea e5 6e 02 f7 cf 79 fc 14 cf 3b dc 7c d8 - 4f eb bb f9 50 ca 90 30 4b 22 19 a7 aa 06 3a ef - a2 c3 c1 98 0e 56 0c d6 4a fe 77 95 85 b6 10 76 - 57 b9 57 85 7e fd e6 01 09 88 ab 7d e4 17 fc 88 - d8 f3 84 c4 e6 e7 2c 3f 94 3e 0c 31 c0 c4 a5 cc - 36 f8 79 d8 a3 ac 9d 7d 59 86 0e aa da 6b 83 bb''', - 'e':'''01 00 01''', - 'd':'''05 6b 04 21 6f e5 f3 54 ac 77 25 0a 4b 6b 0c 85 - 25 a8 5c 59 b0 bd 80 c5 64 50 a2 2d 5f 43 8e 59 - 6a 33 3a a8 75 e2 91 dd 43 f4 8c b8 8b 9d 5f c0 - d4 99 f9 fc d1 c3 97 f9 af c0 70 cd 9e 39 8c 8d - 19 e6 1d b7 c7 41 0a 6b 26 75 df bf 5d 34 5b 80 - 4d 20 1a dd 50 2d 5c e2 df cb 09 1c e9 99 7b be - be 57 30 6f 38 3e 4d 58 81 03 f0 36 f7 e8 5d 19 - 34 d1 52 a3 23 e4 a8 db 45 1d 6f 4a 5b 1b 0f 10 - 2c c1 50 e0 2f ee e2 b8 8d ea 4a d4 c1 ba cc b2 - 4d 84 07 2d 14 e1 d2 4a 67 71 f7 40 8e e3 05 64 - fb 86 d4 39 3a 34 bc f0 b7 88 50 1d 19 33 03 f1 - 3a 22 84 b0 01 f0 f6 49 ea f7 93 28 d4 ac 5c 43 - 0a b4 41 49 20 a9 46 0e d1 b7 bc 40 ec 65 3e 87 - 6d 09 ab c5 09 ae 45 b5 25 19 01 16 a0 c2 61 01 - 84 82 98 50 9c 1c 3b f3 a4 83 e7 27 40 54 e1 5e - 97 07 50 36 e9 89 f6 09 32 80 7b 52 57 75 1e 79''' - }, - # Plaintext - '''8b ba 6b f8 2a 6c 0f 86 d5 f1 75 6e 97 95 68 70 - b0 89 53 b0 6b 4e b2 05 bc 16 94 ee''', - # Ciphertext - '''53 ea 5d c0 8c d2 60 fb 3b 85 85 67 28 7f a9 15 - 52 c3 0b 2f eb fb a2 13 f0 ae 87 70 2d 06 8d 19 - ba b0 7f e5 74 52 3d fb 42 13 9d 68 c3 c5 af ee - e0 bf e4 cb 79 69 cb f3 82 b8 04 d6 e6 13 96 14 - 4e 2d 0e 60 74 1f 89 93 c3 01 4b 58 b9 b1 95 7a - 8b ab cd 23 af 85 4f 4c 35 6f b1 66 2a a7 2b fc - c7 e5 86 55 9d c4 28 0d 16 0c 12 67 85 a7 23 eb - ee be ff 71 f1 15 94 44 0a ae f8 7d 10 79 3a 87 - 74 a2 39 d4 a0 4c 87 fe 14 67 b9 da f8 52 08 ec - 6c 72 55 79 4a 96 cc 29 14 2f 9a 8b d4 18 e3 c1 - fd 67 34 4b 0c d0 82 9d f3 b2 be c6 02 53 19 62 - 93 c6 b3 4d 3f 75 d3 2f 21 3d d4 5c 62 73 d5 05 - ad f4 cc ed 10 57 cb 75 8f c2 6a ee fa 44 12 55 - ed 4e 64 c1 99 ee 07 5e 7f 16 64 61 82 fd b4 64 - 73 9b 68 ab 5d af f0 e6 3e 95 52 01 68 24 f0 54 - bf 4d 3c 8c 90 a9 7b b6 b6 55 32 84 eb 42 9f cc''', - # Random - '''47 e1 ab 71 19 fe e5 6c 95 ee 5e aa d8 6f 40 d0 - aa 63 bd 33''', - SHA1 - ), - ) - - def testEncrypt1(self): - # Verify encryption using all test vectors - for test in self._testData: - # Build the key - comps = [int(rws(test[0][x]), 16) for x in ('n', 'e')] - key = RSA.construct(comps) - - # RNG that takes its random numbers from a pool given - # at initialization - class randGen: - - def __init__(self, data): - self.data = data - self.idx = 0 - - def __call__(self, N): - r = self.data[self.idx:N] - self.idx += N - return r - - # The real test - cipher = PKCS.new(key, test[4], randfunc=randGen(t2b(test[3]))) - ct = cipher.encrypt(t2b(test[1])) - self.assertEqual(ct, t2b(test[2])) - - def testEncrypt2(self): - # Verify that encryption fails if plaintext is too long - pt = '\x00'*(128-2*20-2+1) - cipher = PKCS.new(self.key1024) - self.assertRaises(ValueError, cipher.encrypt, pt) - - def testDecrypt1(self): - # Verify decryption using all test vectors - for test in self._testData: - # Build the key - comps = [int(rws(test[0][x]),16) for x in ('n', 'e', 'd')] - key = RSA.construct(comps) - # The real test - cipher = PKCS.new(key, test[4]) - pt = cipher.decrypt(t2b(test[2])) - self.assertEqual(pt, t2b(test[1])) - - def testDecrypt2(self): - # Simplest possible negative tests - for ct_size in (127, 128, 129): - cipher = PKCS.new(self.key1024) - self.assertRaises(ValueError, cipher.decrypt, bchr(0x00)*ct_size) - - def testEncryptDecrypt1(self): - # Encrypt/Decrypt messages of length [0..128-2*20-2] - for pt_len in range(0, 128-2*20-2): - pt = self.rng(pt_len) - cipher = PKCS.new(self.key1024) - ct = cipher.encrypt(pt) - pt2 = cipher.decrypt(ct) - self.assertEqual(pt, pt2) - - def testEncryptDecrypt2(self): - # Helper function to monitor what's requested from RNG - global asked - - def localRng(N): - global asked - asked += N - return self.rng(N) - - # Verify that OAEP is friendly to all hashes - for hashmod in (MD2, MD5, SHA1, SHA256, RIPEMD160): - # Verify that encrypt() asks for as many random bytes - # as the hash output size - asked = 0 - pt = self.rng(40) - cipher = PKCS.new(self.key1024, hashmod, randfunc=localRng) - ct = cipher.encrypt(pt) - self.assertEqual(cipher.decrypt(ct), pt) - self.assertEqual(asked, hashmod.digest_size) - - def testEncryptDecrypt3(self): - # Verify that OAEP supports labels - pt = self.rng(35) - xlabel = self.rng(22) - cipher = PKCS.new(self.key1024, label=xlabel) - ct = cipher.encrypt(pt) - self.assertEqual(cipher.decrypt(ct), pt) - - def testEncryptDecrypt4(self): - # Verify that encrypt() uses the custom MGF - global mgfcalls - # Helper function to monitor what's requested from MGF - - def newMGF(seed, maskLen): - global mgfcalls - mgfcalls += 1 - return b'\x00' * maskLen - - mgfcalls = 0 - pt = self.rng(32) - cipher = PKCS.new(self.key1024, mgfunc=newMGF) - ct = cipher.encrypt(pt) - self.assertEqual(mgfcalls, 2) - self.assertEqual(cipher.decrypt(ct), pt) - - def testByteArray(self): - pt = b("XER") - cipher = PKCS.new(self.key1024) - ct = cipher.encrypt(bytearray(pt)) - pt2 = cipher.decrypt(bytearray(ct)) - self.assertEqual(pt, pt2) - - def testMemoryview(self): - pt = b("XER") - cipher = PKCS.new(self.key1024) - ct = cipher.encrypt(memoryview(bytearray(pt))) - pt2 = cipher.decrypt(memoryview(bytearray(ct))) - self.assertEqual(pt, pt2) - - -class TestVectorsWycheproof(unittest.TestCase): - - def __init__(self, wycheproof_warnings, skip_slow_tests): - unittest.TestCase.__init__(self) - self._wycheproof_warnings = wycheproof_warnings - self._skip_slow_tests = skip_slow_tests - self._id = "None" - - def load_tests(self, filename): - - def filter_rsa(group): - return RSA.import_key(group['privateKeyPem']) - - def filter_sha(group): - if group['sha'] == "SHA-1": - return SHA1 - elif group['sha'] == "SHA-224": - return SHA224 - elif group['sha'] == "SHA-256": - return SHA256 - elif group['sha'] == "SHA-384": - return SHA384 - elif group['sha'] == "SHA-512": - return SHA512 - else: - raise ValueError("Unknown sha " + group['sha']) - - def filter_mgf(group): - if group['mgfSha'] == "SHA-1": - return lambda x, y: MGF1(x, y, SHA1) - elif group['mgfSha'] == "SHA-224": - return lambda x, y: MGF1(x, y, SHA224) - elif group['mgfSha'] == "SHA-256": - return lambda x, y: MGF1(x, y, SHA256) - elif group['mgfSha'] == "SHA-384": - return lambda x, y: MGF1(x, y, SHA384) - elif group['mgfSha'] == "SHA-512": - return lambda x, y: MGF1(x, y, SHA512) - else: - raise ValueError("Unknown mgf/sha " + group['mgfSha']) - - def filter_algo(group): - return "%s with MGF1/%s" % (group['sha'], group['mgfSha']) - - result = load_test_vectors_wycheproof(("Cipher", "wycheproof"), - filename, - "Wycheproof PKCS#1 OAEP (%s)" % filename, - group_tag={'rsa_key': filter_rsa, - 'hash_mod': filter_sha, - 'mgf': filter_mgf, - 'algo': filter_algo} - ) - return result - - def setUp(self): - self.tv = [] - self.tv.extend(self.load_tests("rsa_oaep_2048_sha1_mgf1sha1_test.json")) - self.tv.extend(self.load_tests("rsa_oaep_2048_sha224_mgf1sha1_test.json")) - self.tv.extend(self.load_tests("rsa_oaep_2048_sha224_mgf1sha224_test.json")) - self.tv.extend(self.load_tests("rsa_oaep_2048_sha256_mgf1sha1_test.json")) - self.tv.extend(self.load_tests("rsa_oaep_2048_sha256_mgf1sha256_test.json")) - self.tv.extend(self.load_tests("rsa_oaep_2048_sha384_mgf1sha1_test.json")) - self.tv.extend(self.load_tests("rsa_oaep_2048_sha384_mgf1sha384_test.json")) - self.tv.extend(self.load_tests("rsa_oaep_2048_sha512_mgf1sha1_test.json")) - self.tv.extend(self.load_tests("rsa_oaep_2048_sha512_mgf1sha512_test.json")) - if not self._skip_slow_tests: - self.tv.extend(self.load_tests("rsa_oaep_3072_sha256_mgf1sha1_test.json")) - self.tv.extend(self.load_tests("rsa_oaep_3072_sha256_mgf1sha256_test.json")) - self.tv.extend(self.load_tests("rsa_oaep_3072_sha512_mgf1sha1_test.json")) - self.tv.extend(self.load_tests("rsa_oaep_3072_sha512_mgf1sha512_test.json")) - self.tv.extend(self.load_tests("rsa_oaep_4096_sha256_mgf1sha1_test.json")) - self.tv.extend(self.load_tests("rsa_oaep_4096_sha256_mgf1sha256_test.json")) - self.tv.extend(self.load_tests("rsa_oaep_4096_sha512_mgf1sha1_test.json")) - self.tv.extend(self.load_tests("rsa_oaep_4096_sha512_mgf1sha512_test.json")) - self.tv.extend(self.load_tests("rsa_oaep_4096_sha512_mgf1sha512_test.json")) - self.tv.extend(self.load_tests("rsa_oaep_misc_test.json")) - - def shortDescription(self): - return self._id - - def warn(self, tv): - if tv.warning and self._wycheproof_warnings: - import warnings - warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment)) - - def test_decrypt(self, tv): - self._id = "Wycheproof Decrypt %s Test #%s" % (tv.algo, tv.id) - - cipher = PKCS.new(tv.rsa_key, hashAlgo=tv.hash_mod, mgfunc=tv.mgf, label=tv.label) - try: - pt = cipher.decrypt(tv.ct) - except ValueError: - assert not tv.valid - else: - assert tv.valid - self.assertEqual(pt, tv.msg) - self.warn(tv) - - def runTest(self): - - for tv in self.tv: - self.test_decrypt(tv) - - -def get_tests(config={}): - skip_slow_tests = not config.get('slow_tests') - wycheproof_warnings = config.get('wycheproof_warnings') - - tests = [] - tests += list_test_cases(PKCS1_OAEP_Tests) - tests += [TestVectorsWycheproof(wycheproof_warnings, skip_slow_tests)] - return tests - - -if __name__ == '__main__': - def suite(): - unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/__init__.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/__init__.py deleted file mode 100644 index 5f5b999..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/__init__.py +++ /dev/null @@ -1,62 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Hash/__init__.py: Self-test for hash modules -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test for hash modules""" - -__revision__ = "$Id$" - -def get_tests(config={}): - tests = [] - from Cryptodome.SelfTest.Hash import test_HMAC; tests += test_HMAC.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_CMAC; tests += test_CMAC.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_MD2; tests += test_MD2.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_MD4; tests += test_MD4.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_MD5; tests += test_MD5.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_RIPEMD160; tests += test_RIPEMD160.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_SHA1; tests += test_SHA1.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_SHA224; tests += test_SHA224.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_SHA256; tests += test_SHA256.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_SHA384; tests += test_SHA384.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_SHA512; tests += test_SHA512.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_SHA3_224; tests += test_SHA3_224.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_SHA3_256; tests += test_SHA3_256.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_SHA3_384; tests += test_SHA3_384.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_SHA3_512; tests += test_SHA3_512.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_keccak; tests += test_keccak.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_SHAKE; tests += test_SHAKE.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_BLAKE2; tests += test_BLAKE2.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_Poly1305; tests += test_Poly1305.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_cSHAKE; tests += test_cSHAKE.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_KMAC; tests += test_KMAC.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_TupleHash; tests += test_TupleHash.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_KangarooTwelve; tests += test_KangarooTwelve.get_tests(config=config) - from Cryptodome.SelfTest.Hash import test_TurboSHAKE; tests += test_TurboSHAKE.get_tests(config=config) - return tests - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/common.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/common.py deleted file mode 100644 index 4ed9234..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/common.py +++ /dev/null @@ -1,290 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Hash/common.py: Common code for Cryptodome.SelfTest.Hash -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-testing for PyCryptodome hash modules""" - -import re -import sys -import unittest -import binascii -import Cryptodome.Hash -from binascii import hexlify, unhexlify -from Cryptodome.Util.py3compat import b, tobytes -from Cryptodome.Util.strxor import strxor_c - -def t2b(hex_string): - shorter = re.sub(br'\s+', b'', tobytes(hex_string)) - return unhexlify(shorter) - - -class HashDigestSizeSelfTest(unittest.TestCase): - - def __init__(self, hashmod, description, expected, extra_params): - unittest.TestCase.__init__(self) - self.hashmod = hashmod - self.expected = expected - self.description = description - self.extra_params = extra_params - - def shortDescription(self): - return self.description - - def runTest(self): - if "truncate" not in self.extra_params: - self.assertTrue(hasattr(self.hashmod, "digest_size")) - self.assertEqual(self.hashmod.digest_size, self.expected) - h = self.hashmod.new(**self.extra_params) - self.assertTrue(hasattr(h, "digest_size")) - self.assertEqual(h.digest_size, self.expected) - - -class HashSelfTest(unittest.TestCase): - - def __init__(self, hashmod, description, expected, input, extra_params): - unittest.TestCase.__init__(self) - self.hashmod = hashmod - self.expected = expected.lower() - self.input = input - self.description = description - self.extra_params = extra_params - - def shortDescription(self): - return self.description - - def runTest(self): - h = self.hashmod.new(**self.extra_params) - h.update(self.input) - - out1 = binascii.b2a_hex(h.digest()) - out2 = h.hexdigest() - - h = self.hashmod.new(self.input, **self.extra_params) - - out3 = h.hexdigest() - out4 = binascii.b2a_hex(h.digest()) - - # PY3K: hexdigest() should return str(), and digest() bytes - self.assertEqual(self.expected, out1) # h = .new(); h.update(data); h.digest() - if sys.version_info[0] == 2: - self.assertEqual(self.expected, out2) # h = .new(); h.update(data); h.hexdigest() - self.assertEqual(self.expected, out3) # h = .new(data); h.hexdigest() - else: - self.assertEqual(self.expected.decode(), out2) # h = .new(); h.update(data); h.hexdigest() - self.assertEqual(self.expected.decode(), out3) # h = .new(data); h.hexdigest() - self.assertEqual(self.expected, out4) # h = .new(data); h.digest() - - # Verify that the .new() method produces a fresh hash object, except - # for MD5 and SHA1, which are hashlib objects. (But test any .new() - # method that does exist.) - if self.hashmod.__name__ not in ('Cryptodome.Hash.MD5', 'Cryptodome.Hash.SHA1') or hasattr(h, 'new'): - h2 = h.new() - h2.update(self.input) - out5 = binascii.b2a_hex(h2.digest()) - self.assertEqual(self.expected, out5) - - -class HashTestOID(unittest.TestCase): - def __init__(self, hashmod, oid, extra_params): - unittest.TestCase.__init__(self) - self.hashmod = hashmod - self.oid = oid - self.extra_params = extra_params - - def runTest(self): - h = self.hashmod.new(**self.extra_params) - self.assertEqual(h.oid, self.oid) - - -class ByteArrayTest(unittest.TestCase): - - def __init__(self, module, extra_params): - unittest.TestCase.__init__(self) - self.module = module - self.extra_params = extra_params - - def runTest(self): - data = b("\x00\x01\x02") - - # Data can be a bytearray (during initialization) - ba = bytearray(data) - - h1 = self.module.new(data, **self.extra_params) - h2 = self.module.new(ba, **self.extra_params) - ba[:1] = b'\xFF' - self.assertEqual(h1.digest(), h2.digest()) - - # Data can be a bytearray (during operation) - ba = bytearray(data) - - h1 = self.module.new(**self.extra_params) - h2 = self.module.new(**self.extra_params) - - h1.update(data) - h2.update(ba) - - ba[:1] = b'\xFF' - self.assertEqual(h1.digest(), h2.digest()) - - -class MemoryViewTest(unittest.TestCase): - - def __init__(self, module, extra_params): - unittest.TestCase.__init__(self) - self.module = module - self.extra_params = extra_params - - def runTest(self): - - data = b"\x00\x01\x02" - - def get_mv_ro(data): - return memoryview(data) - - def get_mv_rw(data): - return memoryview(bytearray(data)) - - for get_mv in get_mv_ro, get_mv_rw: - - # Data can be a memoryview (during initialization) - mv = get_mv(data) - - h1 = self.module.new(data, **self.extra_params) - h2 = self.module.new(mv, **self.extra_params) - if not mv.readonly: - mv[:1] = b'\xFF' - self.assertEqual(h1.digest(), h2.digest()) - - # Data can be a memoryview (during operation) - mv = get_mv(data) - - h1 = self.module.new(**self.extra_params) - h2 = self.module.new(**self.extra_params) - h1.update(data) - h2.update(mv) - if not mv.readonly: - mv[:1] = b'\xFF' - self.assertEqual(h1.digest(), h2.digest()) - - -class MACSelfTest(unittest.TestCase): - - def __init__(self, module, description, result, data, key, params): - unittest.TestCase.__init__(self) - self.module = module - self.result = t2b(result) - self.data = t2b(data) - self.key = t2b(key) - self.params = params - self.description = description - - def shortDescription(self): - return self.description - - def runTest(self): - - result_hex = hexlify(self.result) - - # Verify result - h = self.module.new(self.key, **self.params) - h.update(self.data) - self.assertEqual(self.result, h.digest()) - self.assertEqual(hexlify(self.result).decode('ascii'), h.hexdigest()) - - # Verify that correct MAC does not raise any exception - h.verify(self.result) - h.hexverify(result_hex) - - # Verify that incorrect MAC does raise ValueError exception - wrong_mac = strxor_c(self.result, 255) - self.assertRaises(ValueError, h.verify, wrong_mac) - self.assertRaises(ValueError, h.hexverify, "4556") - - # Verify again, with data passed to new() - h = self.module.new(self.key, self.data, **self.params) - self.assertEqual(self.result, h.digest()) - self.assertEqual(hexlify(self.result).decode('ascii'), h.hexdigest()) - - # Test .copy() - try: - h = self.module.new(self.key, self.data, **self.params) - h2 = h.copy() - h3 = h.copy() - - # Verify that changing the copy does not change the original - h2.update(b"bla") - self.assertEqual(h3.digest(), self.result) - - # Verify that both can reach the same state - h.update(b"bla") - self.assertEqual(h.digest(), h2.digest()) - except NotImplementedError: - pass - - # PY3K: Check that hexdigest() returns str and digest() returns bytes - self.assertTrue(isinstance(h.digest(), type(b""))) - self.assertTrue(isinstance(h.hexdigest(), type(""))) - - # PY3K: Check that .hexverify() accepts bytes or str - h.hexverify(h.hexdigest()) - h.hexverify(h.hexdigest().encode('ascii')) - - -def make_hash_tests(module, module_name, test_data, digest_size, oid=None, - extra_params={}): - tests = [] - for i in range(len(test_data)): - row = test_data[i] - (expected, input) = map(tobytes,row[0:2]) - if len(row) < 3: - description = repr(input) - else: - description = row[2] - name = "%s #%d: %s" % (module_name, i+1, description) - tests.append(HashSelfTest(module, name, expected, input, extra_params)) - - name = "%s #%d: digest_size" % (module_name, len(test_data) + 1) - tests.append(HashDigestSizeSelfTest(module, name, digest_size, extra_params)) - - if oid is not None: - tests.append(HashTestOID(module, oid, extra_params)) - - tests.append(ByteArrayTest(module, extra_params)) - - tests.append(MemoryViewTest(module, extra_params)) - - return tests - - -def make_mac_tests(module, module_name, test_data): - tests = [] - for i, row in enumerate(test_data): - if len(row) == 4: - (key, data, results, description, params) = list(row) + [ {} ] - else: - (key, data, results, description, params) = row - name = "%s #%d: %s" % (module_name, i+1, description) - tests.append(MACSelfTest(module, name, results, data, key, params)) - return tests - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_BLAKE2.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_BLAKE2.py deleted file mode 100644 index e5ed63b..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_BLAKE2.py +++ /dev/null @@ -1,482 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import os -import re -import unittest -import warnings -from binascii import unhexlify, hexlify - -from Cryptodome.Util.py3compat import tobytes -from Cryptodome.Util.strxor import strxor_c -from Cryptodome.SelfTest.st_common import list_test_cases - -from Cryptodome.Hash import BLAKE2b, BLAKE2s - - -class Blake2Test(unittest.TestCase): - - def test_new_positive(self): - - h = self.BLAKE2.new(digest_bits=self.max_bits) - for new_func in self.BLAKE2.new, h.new: - - for dbits in range(8, self.max_bits + 1, 8): - hobj = new_func(digest_bits=dbits) - self.assertEqual(hobj.digest_size, dbits // 8) - - for dbytes in range(1, self.max_bytes + 1): - hobj = new_func(digest_bytes=dbytes) - self.assertEqual(hobj.digest_size, dbytes) - - digest1 = new_func(data=b"\x90", digest_bytes=self.max_bytes).digest() - digest2 = new_func(digest_bytes=self.max_bytes).update(b"\x90").digest() - self.assertEqual(digest1, digest2) - - new_func(data=b"A", key=b"5", digest_bytes=self.max_bytes) - - hobj = h.new() - self.assertEqual(hobj.digest_size, self.max_bytes) - - def test_new_negative(self): - - h = self.BLAKE2.new(digest_bits=self.max_bits) - for new_func in self.BLAKE2.new, h.new: - self.assertRaises(TypeError, new_func, - digest_bytes=self.max_bytes, - digest_bits=self.max_bits) - self.assertRaises(ValueError, new_func, digest_bytes=0) - self.assertRaises(ValueError, new_func, - digest_bytes=self.max_bytes + 1) - self.assertRaises(ValueError, new_func, digest_bits=7) - self.assertRaises(ValueError, new_func, digest_bits=15) - self.assertRaises(ValueError, new_func, - digest_bits=self.max_bits + 1) - self.assertRaises(TypeError, new_func, - digest_bytes=self.max_bytes, - key=u"string") - self.assertRaises(TypeError, new_func, - digest_bytes=self.max_bytes, - data=u"string") - - def test_default_digest_size(self): - digest = self.BLAKE2.new(data=b'abc').digest() - self.assertEqual(len(digest), self.max_bytes) - - def test_update(self): - pieces = [b"\x0A" * 200, b"\x14" * 300] - h = self.BLAKE2.new(digest_bytes=self.max_bytes) - h.update(pieces[0]).update(pieces[1]) - digest = h.digest() - h = self.BLAKE2.new(digest_bytes=self.max_bytes) - h.update(pieces[0] + pieces[1]) - self.assertEqual(h.digest(), digest) - - def test_update_negative(self): - h = self.BLAKE2.new(digest_bytes=self.max_bytes) - self.assertRaises(TypeError, h.update, u"string") - - def test_digest(self): - h = self.BLAKE2.new(digest_bytes=self.max_bytes) - digest = h.digest() - - # hexdigest does not change the state - self.assertEqual(h.digest(), digest) - # digest returns a byte string - self.assertTrue(isinstance(digest, type(b"digest"))) - - def test_update_after_digest(self): - msg = b"rrrrttt" - - # Normally, update() cannot be done after digest() - h = self.BLAKE2.new(digest_bits=256, data=msg[:4]) - dig1 = h.digest() - self.assertRaises(TypeError, h.update, msg[4:]) - dig2 = self.BLAKE2.new(digest_bits=256, data=msg).digest() - - # With the proper flag, it is allowed - h = self.BLAKE2.new(digest_bits=256, data=msg[:4], update_after_digest=True) - self.assertEqual(h.digest(), dig1) - # ... and the subsequent digest applies to the entire message - # up to that point - h.update(msg[4:]) - self.assertEqual(h.digest(), dig2) - - def test_hex_digest(self): - mac = self.BLAKE2.new(digest_bits=self.max_bits) - digest = mac.digest() - hexdigest = mac.hexdigest() - - # hexdigest is equivalent to digest - self.assertEqual(hexlify(digest), tobytes(hexdigest)) - # hexdigest does not change the state - self.assertEqual(mac.hexdigest(), hexdigest) - # hexdigest returns a string - self.assertTrue(isinstance(hexdigest, type("digest"))) - - def test_verify(self): - h = self.BLAKE2.new(digest_bytes=self.max_bytes, key=b"4") - mac = h.digest() - h.verify(mac) - wrong_mac = strxor_c(mac, 255) - self.assertRaises(ValueError, h.verify, wrong_mac) - - def test_hexverify(self): - h = self.BLAKE2.new(digest_bytes=self.max_bytes, key=b"4") - mac = h.hexdigest() - h.hexverify(mac) - self.assertRaises(ValueError, h.hexverify, "4556") - - def test_oid(self): - - prefix = "1.3.6.1.4.1.1722.12.2." + self.oid_variant + "." - - for digest_bits in self.digest_bits_oid: - h = self.BLAKE2.new(digest_bits=digest_bits) - self.assertEqual(h.oid, prefix + str(digest_bits // 8)) - - h = self.BLAKE2.new(digest_bits=digest_bits, key=b"secret") - self.assertRaises(AttributeError, lambda: h.oid) - - for digest_bits in (8, self.max_bits): - if digest_bits in self.digest_bits_oid: - continue - self.assertRaises(AttributeError, lambda: h.oid) - - def test_bytearray(self): - - key = b'0' * 16 - data = b"\x00\x01\x02" - - # Data and key can be a bytearray (during initialization) - key_ba = bytearray(key) - data_ba = bytearray(data) - - h1 = self.BLAKE2.new(data=data, key=key) - h2 = self.BLAKE2.new(data=data_ba, key=key_ba) - key_ba[:1] = b'\xFF' - data_ba[:1] = b'\xFF' - - self.assertEqual(h1.digest(), h2.digest()) - - # Data can be a bytearray (during operation) - data_ba = bytearray(data) - - h1 = self.BLAKE2.new() - h2 = self.BLAKE2.new() - h1.update(data) - h2.update(data_ba) - data_ba[:1] = b'\xFF' - - self.assertEqual(h1.digest(), h2.digest()) - - def test_memoryview(self): - - key = b'0' * 16 - data = b"\x00\x01\x02" - - def get_mv_ro(data): - return memoryview(data) - - def get_mv_rw(data): - return memoryview(bytearray(data)) - - for get_mv in (get_mv_ro, get_mv_rw): - - # Data and key can be a memoryview (during initialization) - key_mv = get_mv(key) - data_mv = get_mv(data) - - h1 = self.BLAKE2.new(data=data, key=key) - h2 = self.BLAKE2.new(data=data_mv, key=key_mv) - if not data_mv.readonly: - data_mv[:1] = b'\xFF' - key_mv[:1] = b'\xFF' - - self.assertEqual(h1.digest(), h2.digest()) - - # Data can be a memoryview (during operation) - data_mv = get_mv(data) - - h1 = self.BLAKE2.new() - h2 = self.BLAKE2.new() - h1.update(data) - h2.update(data_mv) - if not data_mv.readonly: - data_mv[:1] = b'\xFF' - - self.assertEqual(h1.digest(), h2.digest()) - - -class Blake2bTest(Blake2Test): - #: Module - BLAKE2 = BLAKE2b - #: Max output size (in bits) - max_bits = 512 - #: Max output size (in bytes) - max_bytes = 64 - #: Bit size of the digests for which an ASN OID exists - digest_bits_oid = (160, 256, 384, 512) - # http://tools.ietf.org/html/draft-saarinen-blake2-02 - oid_variant = "1" - - -class Blake2sTest(Blake2Test): - #: Module - BLAKE2 = BLAKE2s - #: Max output size (in bits) - max_bits = 256 - #: Max output size (in bytes) - max_bytes = 32 - #: Bit size of the digests for which an ASN OID exists - digest_bits_oid = (128, 160, 224, 256) - # http://tools.ietf.org/html/draft-saarinen-blake2-02 - oid_variant = "2" - - -class Blake2OfficialTestVector(unittest.TestCase): - - def _load_tests(self, test_vector_file): - expected = "in" - test_vectors = [] - with open(test_vector_file, "rt") as test_vector_fd: - for line_number, line in enumerate(test_vector_fd): - - if line.strip() == "" or line.startswith("#"): - continue - - res = re.match("%s:\t([0-9A-Fa-f]*)" % expected, line) - if not res: - raise ValueError("Incorrect test vector format (line %d)" - % line_number) - - if res.group(1): - bin_value = unhexlify(tobytes(res.group(1))) - else: - bin_value = b"" - if expected == "in": - input_data = bin_value - expected = "key" - elif expected == "key": - key = bin_value - expected = "hash" - else: - result = bin_value - expected = "in" - test_vectors.append((input_data, key, result)) - return test_vectors - - def setUp(self): - - dir_comps = ("Hash", self.name) - file_name = self.name.lower() + "-test.txt" - self.description = "%s tests" % self.name - - try: - import pycryptodome_test_vectors # type: ignore - except ImportError: - warnings.warn("Warning: skipping extended tests for %s" % self.name, - UserWarning) - self.test_vectors = [] - return - - init_dir = os.path.dirname(pycryptodome_test_vectors.__file__) - full_file_name = os.path.join(os.path.join(init_dir, *dir_comps), file_name) - self.test_vectors = self._load_tests(full_file_name) - - def runTest(self): - for (input_data, key, result) in self.test_vectors: - mac = self.BLAKE2.new(key=key, digest_bytes=self.max_bytes) - mac.update(input_data) - self.assertEqual(mac.digest(), result) - - -class Blake2bOfficialTestVector(Blake2OfficialTestVector): - #: Module - BLAKE2 = BLAKE2b - #: Hash name - name = "BLAKE2b" - #: Max digest size - max_bytes = 64 - - -class Blake2sOfficialTestVector(Blake2OfficialTestVector): - #: Module - BLAKE2 = BLAKE2s - #: Hash name - name = "BLAKE2s" - #: Max digest size - max_bytes = 32 - - -class Blake2TestVector1(unittest.TestCase): - - def _load_tests(self, test_vector_file): - test_vectors = [] - with open(test_vector_file, "rt") as test_vector_fd: - for line_number, line in enumerate(test_vector_fd): - if line.strip() == "" or line.startswith("#"): - continue - res = re.match("digest: ([0-9A-Fa-f]*)", line) - if not res: - raise ValueError("Incorrect test vector format (line %d)" - % line_number) - - test_vectors.append(unhexlify(tobytes(res.group(1)))) - return test_vectors - - def setUp(self): - dir_comps = ("Hash", self.name) - file_name = "tv1.txt" - self.description = "%s tests" % self.name - - try: - import pycryptodome_test_vectors - except ImportError: - warnings.warn("Warning: skipping extended tests for %s" % self.name, - UserWarning) - self.test_vectors = [] - return - - init_dir = os.path.dirname(pycryptodome_test_vectors.__file__) - full_file_name = os.path.join(os.path.join(init_dir, *dir_comps), file_name) - self.test_vectors = self._load_tests(full_file_name) - - def runTest(self): - - for tv in self.test_vectors: - digest_bytes = len(tv) - next_data = b"" - for _ in range(100): - h = self.BLAKE2.new(digest_bytes=digest_bytes) - h.update(next_data) - next_data = h.digest() + next_data - self.assertEqual(h.digest(), tv) - - -class Blake2bTestVector1(Blake2TestVector1): - #: Module - BLAKE2 = BLAKE2b - #: Hash name - name = "BLAKE2b" - - -class Blake2sTestVector1(Blake2TestVector1): - #: Module - BLAKE2 = BLAKE2s - #: Hash name - name = "BLAKE2s" - - -class Blake2TestVector2(unittest.TestCase): - - def _load_tests(self, test_vector_file): - test_vectors = [] - with open(test_vector_file, "rt") as test_vector_fd: - for line_number, line in enumerate(test_vector_fd): - if line.strip() == "" or line.startswith("#"): - continue - res = re.match(r"digest\(([0-9]+)\): ([0-9A-Fa-f]*)", line) - if not res: - raise ValueError("Incorrect test vector format (line %d)" - % line_number) - key_size = int(res.group(1)) - result = unhexlify(tobytes(res.group(2))) - test_vectors.append((key_size, result)) - return test_vectors - - def setUp(self): - dir_comps = ("Hash", self.name) - file_name = "tv2.txt" - self.description = "%s tests" % self.name - - try: - import pycryptodome_test_vectors # type: ignore - except ImportError: - warnings.warn("Warning: skipping extended tests for %s" % self.name, - UserWarning) - self.test_vectors = [] - return - - init_dir = os.path.dirname(pycryptodome_test_vectors.__file__) - full_file_name = os.path.join(os.path.join(init_dir, *dir_comps), file_name) - self.test_vectors = self._load_tests(full_file_name) - - def runTest(self): - - for key_size, result in self.test_vectors: - next_data = b"" - for _ in range(100): - h = self.BLAKE2.new(digest_bytes=self.max_bytes, - key=b"A" * key_size) - h.update(next_data) - next_data = h.digest() + next_data - self.assertEqual(h.digest(), result) - - -class Blake2bTestVector2(Blake2TestVector1): - #: Module - BLAKE2 = BLAKE2b - #: Hash name - name = "BLAKE2b" - #: Max digest size in bytes - max_bytes = 64 - - -class Blake2sTestVector2(Blake2TestVector1): - #: Module - BLAKE2 = BLAKE2s - #: Hash name - name = "BLAKE2s" - #: Max digest size in bytes - max_bytes = 32 - - -def get_tests(config={}): - tests = [] - - tests += list_test_cases(Blake2bTest) - tests.append(Blake2bOfficialTestVector()) - tests.append(Blake2bTestVector1()) - tests.append(Blake2bTestVector2()) - - tests += list_test_cases(Blake2sTest) - tests.append(Blake2sOfficialTestVector()) - tests.append(Blake2sTestVector1()) - tests.append(Blake2sTestVector2()) - - return tests - - -if __name__ == '__main__': - import unittest - def suite(): - return unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_CMAC.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_CMAC.py deleted file mode 100644 index f88f1cd..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_CMAC.py +++ /dev/null @@ -1,448 +0,0 @@ -# -# SelfTest/Hash/CMAC.py: Self-test for the CMAC module -# -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -"""Self-test suite for Cryptodome.Hash.CMAC""" - -import json -import unittest -from binascii import unhexlify - -from Cryptodome.Util.py3compat import tobytes - -from Cryptodome.Hash import CMAC -from Cryptodome.Cipher import AES, DES3 -from Cryptodome.Hash import SHAKE128 - -from Cryptodome.Util.strxor import strxor - -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.SelfTest.loader import load_test_vectors_wycheproof - -# This is a list of (key, data, result, description, module) tuples. -test_data = [ - - ## Test vectors from RFC 4493 ## - ## The are also in NIST SP 800 38B D.2 ## - ( '2b7e151628aed2a6abf7158809cf4f3c', - '', - 'bb1d6929e95937287fa37d129b756746', - 'RFC 4493 #1', - AES - ), - - ( '2b7e151628aed2a6abf7158809cf4f3c', - '6bc1bee22e409f96e93d7e117393172a', - '070a16b46b4d4144f79bdd9dd04a287c', - 'RFC 4493 #2', - AES - ), - - ( '2b7e151628aed2a6abf7158809cf4f3c', - '6bc1bee22e409f96e93d7e117393172a'+ - 'ae2d8a571e03ac9c9eb76fac45af8e51'+ - '30c81c46a35ce411', - 'dfa66747de9ae63030ca32611497c827', - 'RFC 4493 #3', - AES - ), - - ( '2b7e151628aed2a6abf7158809cf4f3c', - '6bc1bee22e409f96e93d7e117393172a'+ - 'ae2d8a571e03ac9c9eb76fac45af8e51'+ - '30c81c46a35ce411e5fbc1191a0a52ef'+ - 'f69f2445df4f9b17ad2b417be66c3710', - '51f0bebf7e3b9d92fc49741779363cfe', - 'RFC 4493 #4', - AES - ), - - ## The rest of Appendix D of NIST SP 800 38B - ## was not totally correct. - ## Values in Examples 14, 15, 18, and 19 were wrong. - ## The updated test values are published in: - ## http://csrc.nist.gov/publications/nistpubs/800-38B/Updated_CMAC_Examples.pdf - - ( '8e73b0f7da0e6452c810f32b809079e5'+ - '62f8ead2522c6b7b', - '', - 'd17ddf46adaacde531cac483de7a9367', - 'NIST SP 800 38B D.2 Example 5', - AES - ), - - ( '8e73b0f7da0e6452c810f32b809079e5'+ - '62f8ead2522c6b7b', - '6bc1bee22e409f96e93d7e117393172a', - '9e99a7bf31e710900662f65e617c5184', - 'NIST SP 800 38B D.2 Example 6', - AES - ), - - ( '8e73b0f7da0e6452c810f32b809079e5'+ - '62f8ead2522c6b7b', - '6bc1bee22e409f96e93d7e117393172a'+ - 'ae2d8a571e03ac9c9eb76fac45af8e51'+ - '30c81c46a35ce411', - '8a1de5be2eb31aad089a82e6ee908b0e', - 'NIST SP 800 38B D.2 Example 7', - AES - ), - - ( '8e73b0f7da0e6452c810f32b809079e5'+ - '62f8ead2522c6b7b', - '6bc1bee22e409f96e93d7e117393172a'+ - 'ae2d8a571e03ac9c9eb76fac45af8e51'+ - '30c81c46a35ce411e5fbc1191a0a52ef'+ - 'f69f2445df4f9b17ad2b417be66c3710', - 'a1d5df0eed790f794d77589659f39a11', - 'NIST SP 800 38B D.2 Example 8', - AES - ), - - ( '603deb1015ca71be2b73aef0857d7781'+ - '1f352c073b6108d72d9810a30914dff4', - '', - '028962f61b7bf89efc6b551f4667d983', - 'NIST SP 800 38B D.3 Example 9', - AES - ), - - ( '603deb1015ca71be2b73aef0857d7781'+ - '1f352c073b6108d72d9810a30914dff4', - '6bc1bee22e409f96e93d7e117393172a', - '28a7023f452e8f82bd4bf28d8c37c35c', - 'NIST SP 800 38B D.3 Example 10', - AES - ), - - ( '603deb1015ca71be2b73aef0857d7781'+ - '1f352c073b6108d72d9810a30914dff4', - '6bc1bee22e409f96e93d7e117393172a'+ - 'ae2d8a571e03ac9c9eb76fac45af8e51'+ - '30c81c46a35ce411', - 'aaf3d8f1de5640c232f5b169b9c911e6', - 'NIST SP 800 38B D.3 Example 11', - AES - ), - - ( '603deb1015ca71be2b73aef0857d7781'+ - '1f352c073b6108d72d9810a30914dff4', - '6bc1bee22e409f96e93d7e117393172a'+ - 'ae2d8a571e03ac9c9eb76fac45af8e51'+ - '30c81c46a35ce411e5fbc1191a0a52ef'+ - 'f69f2445df4f9b17ad2b417be66c3710', - 'e1992190549f6ed5696a2c056c315410', - 'NIST SP 800 38B D.3 Example 12', - AES - ), - - ( '8aa83bf8cbda1062'+ - '0bc1bf19fbb6cd58'+ - 'bc313d4a371ca8b5', - '', - 'b7a688e122ffaf95', - 'NIST SP 800 38B D.4 Example 13', - DES3 - ), - - ( '8aa83bf8cbda1062'+ - '0bc1bf19fbb6cd58'+ - 'bc313d4a371ca8b5', - '6bc1bee22e409f96', - '8e8f293136283797', - 'NIST SP 800 38B D.4 Example 14', - DES3 - ), - - ( '8aa83bf8cbda1062'+ - '0bc1bf19fbb6cd58'+ - 'bc313d4a371ca8b5', - '6bc1bee22e409f96'+ - 'e93d7e117393172a'+ - 'ae2d8a57', - '743ddbe0ce2dc2ed', - 'NIST SP 800 38B D.4 Example 15', - DES3 - ), - - ( '8aa83bf8cbda1062'+ - '0bc1bf19fbb6cd58'+ - 'bc313d4a371ca8b5', - '6bc1bee22e409f96'+ - 'e93d7e117393172a'+ - 'ae2d8a571e03ac9c'+ - '9eb76fac45af8e51', - '33e6b1092400eae5', - 'NIST SP 800 38B D.4 Example 16', - DES3 - ), - - ( '4cf15134a2850dd5'+ - '8a3d10ba80570d38', - '', - 'bd2ebf9a3ba00361', - 'NIST SP 800 38B D.7 Example 17', - DES3 - ), - - ( '4cf15134a2850dd5'+ - '8a3d10ba80570d38', - '6bc1bee22e409f96', - '4ff2ab813c53ce83', - 'NIST SP 800 38B D.7 Example 18', - DES3 - ), - - ( '4cf15134a2850dd5'+ - '8a3d10ba80570d38', - '6bc1bee22e409f96'+ - 'e93d7e117393172a'+ - 'ae2d8a57', - '62dd1b471902bd4e', - 'NIST SP 800 38B D.7 Example 19', - DES3 - ), - - ( '4cf15134a2850dd5'+ - '8a3d10ba80570d38', - '6bc1bee22e409f96'+ - 'e93d7e117393172a'+ - 'ae2d8a571e03ac9c'+ - '9eb76fac45af8e51', - '31b1e431dabc4eb8', - 'NIST SP 800 38B D.7 Example 20', - DES3 - ), - -] - - -def get_tag_random(tag, length): - return SHAKE128.new(data=tobytes(tag)).read(length) - - -class TestCMAC(unittest.TestCase): - - def test_internal_caching(self): - """Verify that internal caching is implemented correctly""" - - data_to_mac = get_tag_random("data_to_mac", 128) - key = get_tag_random("key", 16) - ref_mac = CMAC.new(key, msg=data_to_mac, ciphermod=AES).digest() - - # Break up in chunks of different length - # The result must always be the same - for chunk_length in 1, 2, 3, 7, 10, 13, 16, 40, 80, 128: - - chunks = [data_to_mac[i:i+chunk_length] for i in - range(0, len(data_to_mac), chunk_length)] - - mac = CMAC.new(key, ciphermod=AES) - for chunk in chunks: - mac.update(chunk) - self.assertEqual(ref_mac, mac.digest()) - - def test_update_after_digest(self): - msg = b"rrrrttt" - key = b"4" * 16 - - # Normally, update() cannot be done after digest() - h = CMAC.new(key, msg[:4], ciphermod=AES) - dig1 = h.digest() - self.assertRaises(TypeError, h.update, msg[4:]) - dig2 = CMAC.new(key, msg, ciphermod=AES).digest() - - # With the proper flag, it is allowed - h2 = CMAC.new(key, msg[:4], ciphermod=AES, update_after_digest=True) - self.assertEqual(h2.digest(), dig1) - # ... and the subsequent digest applies to the entire message - # up to that point - h2.update(msg[4:]) - self.assertEqual(h2.digest(), dig2) - - -class ByteArrayTests(unittest.TestCase): - - def runTest(self): - - key = b"0" * 16 - data = b"\x00\x01\x02" - - # Data and key can be a bytearray (during initialization) - key_ba = bytearray(key) - data_ba = bytearray(data) - - h1 = CMAC.new(key, data, ciphermod=AES) - h2 = CMAC.new(key_ba, data_ba, ciphermod=AES) - key_ba[:1] = b'\xFF' - data_ba[:1] = b'\xFF' - self.assertEqual(h1.digest(), h2.digest()) - - # Data can be a bytearray (during operation) - key_ba = bytearray(key) - data_ba = bytearray(data) - - h1 = CMAC.new(key, ciphermod=AES) - h2 = CMAC.new(key, ciphermod=AES) - h1.update(data) - h2.update(data_ba) - data_ba[:1] = b'\xFF' - self.assertEqual(h1.digest(), h2.digest()) - - -class MemoryViewTests(unittest.TestCase): - - def runTest(self): - - key = b"0" * 16 - data = b"\x00\x01\x02" - - def get_mv_ro(data): - return memoryview(data) - - def get_mv_rw(data): - return memoryview(bytearray(data)) - - for get_mv in (get_mv_ro, get_mv_rw): - - # Data and key can be a memoryview (during initialization) - key_mv = get_mv(key) - data_mv = get_mv(data) - - h1 = CMAC.new(key, data, ciphermod=AES) - h2 = CMAC.new(key_mv, data_mv, ciphermod=AES) - if not data_mv.readonly: - key_mv[:1] = b'\xFF' - data_mv[:1] = b'\xFF' - self.assertEqual(h1.digest(), h2.digest()) - - # Data can be a memoryview (during operation) - data_mv = get_mv(data) - - h1 = CMAC.new(key, ciphermod=AES) - h2 = CMAC.new(key, ciphermod=AES) - h1.update(data) - h2.update(data_mv) - if not data_mv.readonly: - data_mv[:1] = b'\xFF' - self.assertEqual(h1.digest(), h2.digest()) - - -class TestVectorsWycheproof(unittest.TestCase): - - def __init__(self, wycheproof_warnings): - unittest.TestCase.__init__(self) - self._wycheproof_warnings = wycheproof_warnings - self._id = "None" - - def setUp(self): - - def filter_tag(group): - return group['tagSize'] // 8 - - self.tv = load_test_vectors_wycheproof(("Hash", "wycheproof"), - "aes_cmac_test.json", - "Wycheproof CMAC", - group_tag={'tag_size': filter_tag}) - - def shortDescription(self): - return self._id - - def warn(self, tv): - if tv.warning and self._wycheproof_warnings: - import warnings - warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment)) - - def test_create_mac(self, tv): - self._id = "Wycheproof MAC creation Test #" + str(tv.id) - - try: - tag = CMAC.new(tv.key, tv.msg, ciphermod=AES, mac_len=tv.tag_size).digest() - except ValueError as e: - if len(tv.key) not in (16, 24, 32) and "key length" in str(e): - return - raise e - if tv.valid: - self.assertEqual(tag, tv.tag) - self.warn(tv) - - def test_verify_mac(self, tv): - self._id = "Wycheproof MAC verification Test #" + str(tv.id) - - try: - mac = CMAC.new(tv.key, tv.msg, ciphermod=AES, mac_len=tv.tag_size) - except ValueError as e: - if len(tv.key) not in (16, 24, 32) and "key length" in str(e): - return - raise e - try: - mac.verify(tv.tag) - except ValueError: - assert not tv.valid - else: - assert tv.valid - self.warn(tv) - - def runTest(self): - - for tv in self.tv: - self.test_create_mac(tv) - self.test_verify_mac(tv) - - -def get_tests(config={}): - global test_data - import types - from .common import make_mac_tests - - wycheproof_warnings = config.get('wycheproof_warnings') - - # Add new() parameters to the back of each test vector - params_test_data = [] - for row in test_data: - t = list(row) - t[4] = dict(ciphermod=t[4]) - params_test_data.append(t) - - tests = make_mac_tests(CMAC, "CMAC", params_test_data) - tests.append(ByteArrayTests()) - tests.append(list_test_cases(TestCMAC)) - tests.append(MemoryViewTests()) - tests += [ TestVectorsWycheproof(wycheproof_warnings) ] - return tests - - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_HMAC.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_HMAC.py deleted file mode 100644 index ecec1a8..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_HMAC.py +++ /dev/null @@ -1,548 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Hash/HMAC.py: Self-test for the HMAC module -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Hash.HMAC""" - -import unittest -from binascii import hexlify -from Cryptodome.Util.py3compat import tostr, tobytes - -from Cryptodome.Hash import (HMAC, MD5, SHA1, SHA256, - SHA224, SHA384, SHA512, - RIPEMD160, - SHA3_224, SHA3_256, SHA3_384, SHA3_512) - - -hash_modules = dict(MD5=MD5, SHA1=SHA1, SHA256=SHA256, - SHA224=SHA224, SHA384=SHA384, SHA512=SHA512, - RIPEMD160=RIPEMD160, - SHA3_224=SHA3_224, SHA3_256=SHA3_256, - SHA3_384=SHA3_384, SHA3_512=SHA3_512) - -default_hash = None - -def xl(text): - return tostr(hexlify(tobytes(text))) - -# This is a list of (key, data, results, description) tuples. -test_data = [ - ## Test vectors from RFC 2202 ## - # Test that the default hashmod is MD5 - ('0b' * 16, - '4869205468657265', - dict(default_hash='9294727a3638bb1c13f48ef8158bfc9d'), - 'default-is-MD5'), - - # Test case 1 (MD5) - ('0b' * 16, - '4869205468657265', - dict(MD5='9294727a3638bb1c13f48ef8158bfc9d'), - 'RFC 2202 #1-MD5 (HMAC-MD5)'), - - # Test case 1 (SHA1) - ('0b' * 20, - '4869205468657265', - dict(SHA1='b617318655057264e28bc0b6fb378c8ef146be00'), - 'RFC 2202 #1-SHA1 (HMAC-SHA1)'), - - # Test case 2 - ('4a656665', - '7768617420646f2079612077616e7420666f72206e6f7468696e673f', - dict(MD5='750c783e6ab0b503eaa86e310a5db738', - SHA1='effcdf6ae5eb2fa2d27416d5f184df9c259a7c79'), - 'RFC 2202 #2 (HMAC-MD5/SHA1)'), - - # Test case 3 (MD5) - ('aa' * 16, - 'dd' * 50, - dict(MD5='56be34521d144c88dbb8c733f0e8b3f6'), - 'RFC 2202 #3-MD5 (HMAC-MD5)'), - - # Test case 3 (SHA1) - ('aa' * 20, - 'dd' * 50, - dict(SHA1='125d7342b9ac11cd91a39af48aa17b4f63f175d3'), - 'RFC 2202 #3-SHA1 (HMAC-SHA1)'), - - # Test case 4 - ('0102030405060708090a0b0c0d0e0f10111213141516171819', - 'cd' * 50, - dict(MD5='697eaf0aca3a3aea3a75164746ffaa79', - SHA1='4c9007f4026250c6bc8414f9bf50c86c2d7235da'), - 'RFC 2202 #4 (HMAC-MD5/SHA1)'), - - # Test case 5 (MD5) - ('0c' * 16, - '546573742057697468205472756e636174696f6e', - dict(MD5='56461ef2342edc00f9bab995690efd4c'), - 'RFC 2202 #5-MD5 (HMAC-MD5)'), - - # Test case 5 (SHA1) - # NB: We do not implement hash truncation, so we only test the full hash here. - ('0c' * 20, - '546573742057697468205472756e636174696f6e', - dict(SHA1='4c1a03424b55e07fe7f27be1d58bb9324a9a5a04'), - 'RFC 2202 #5-SHA1 (HMAC-SHA1)'), - - # Test case 6 - ('aa' * 80, - '54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a' - + '65204b6579202d2048617368204b6579204669727374', - dict(MD5='6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd', - SHA1='aa4ae5e15272d00e95705637ce8a3b55ed402112'), - 'RFC 2202 #6 (HMAC-MD5/SHA1)'), - - # Test case 7 - ('aa' * 80, - '54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a' - + '65204b657920616e64204c6172676572205468616e204f6e6520426c6f636b2d' - + '53697a652044617461', - dict(MD5='6f630fad67cda0ee1fb1f562db3aa53e', - SHA1='e8e99d0f45237d786d6bbaa7965c7808bbff1a91'), - 'RFC 2202 #7 (HMAC-MD5/SHA1)'), - - ## Test vectors from RFC 4231 ## - # 4.2. Test Case 1 - ('0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b', - '4869205468657265', - dict(SHA256=''' - b0344c61d8db38535ca8afceaf0bf12b - 881dc200c9833da726e9376c2e32cff7 - '''), - 'RFC 4231 #1 (HMAC-SHA256)'), - - # 4.3. Test Case 2 - Test with a key shorter than the length of the HMAC - # output. - ('4a656665', - '7768617420646f2079612077616e7420666f72206e6f7468696e673f', - dict(SHA256=''' - 5bdcc146bf60754e6a042426089575c7 - 5a003f089d2739839dec58b964ec3843 - '''), - 'RFC 4231 #2 (HMAC-SHA256)'), - - # 4.4. Test Case 3 - Test with a combined length of key and data that is - # larger than 64 bytes (= block-size of SHA-224 and SHA-256). - ('aa' * 20, - 'dd' * 50, - dict(SHA256=''' - 773ea91e36800e46854db8ebd09181a7 - 2959098b3ef8c122d9635514ced565fe - '''), - 'RFC 4231 #3 (HMAC-SHA256)'), - - # 4.5. Test Case 4 - Test with a combined length of key and data that is - # larger than 64 bytes (= block-size of SHA-224 and SHA-256). - ('0102030405060708090a0b0c0d0e0f10111213141516171819', - 'cd' * 50, - dict(SHA256=''' - 82558a389a443c0ea4cc819899f2083a - 85f0faa3e578f8077a2e3ff46729665b - '''), - 'RFC 4231 #4 (HMAC-SHA256)'), - - # 4.6. Test Case 5 - Test with a truncation of output to 128 bits. - # - # Not included because we do not implement hash truncation. - # - - # 4.7. Test Case 6 - Test with a key larger than 128 bytes (= block-size of - # SHA-384 and SHA-512). - ('aa' * 131, - '54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a' - + '65204b6579202d2048617368204b6579204669727374', - dict(SHA256=''' - 60e431591ee0b67f0d8a26aacbf5b77f - 8e0bc6213728c5140546040f0ee37f54 - '''), - 'RFC 4231 #6 (HMAC-SHA256)'), - - # 4.8. Test Case 7 - Test with a key and data that is larger than 128 bytes - # (= block-size of SHA-384 and SHA-512). - ('aa' * 131, - '5468697320697320612074657374207573696e672061206c6172676572207468' - + '616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074' - + '68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565' - + '647320746f20626520686173686564206265666f7265206265696e6720757365' - + '642062792074686520484d414320616c676f726974686d2e', - dict(SHA256=''' - 9b09ffa71b942fcb27635fbcd5b0e944 - bfdc63644f0713938a7f51535c3a35e2 - '''), - 'RFC 4231 #7 (HMAC-SHA256)'), - - # Test case 8 (SHA224) - ('4a656665', - '7768617420646f2079612077616e74' - + '20666f72206e6f7468696e673f', - dict(SHA224='a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44'), - 'RFC 4634 8.4 SHA224 (HMAC-SHA224)'), - - # Test case 9 (SHA384) - ('4a656665', - '7768617420646f2079612077616e74' - + '20666f72206e6f7468696e673f', - dict(SHA384='af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649'), - 'RFC 4634 8.4 SHA384 (HMAC-SHA384)'), - - # Test case 10 (SHA512) - ('4a656665', - '7768617420646f2079612077616e74' - + '20666f72206e6f7468696e673f', - dict(SHA512='164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737'), - 'RFC 4634 8.4 SHA512 (HMAC-SHA512)'), - - # Test case 11 (RIPEMD) - ('0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b', - xl("Hi There"), - dict(RIPEMD160='24cb4bd67d20fc1a5d2ed7732dcc39377f0a5668'), - 'RFC 2286 #1 (HMAC-RIPEMD)'), - - # Test case 12 (RIPEMD) - (xl("Jefe"), - xl("what do ya want for nothing?"), - dict(RIPEMD160='dda6c0213a485a9e24f4742064a7f033b43c4069'), - 'RFC 2286 #2 (HMAC-RIPEMD)'), - - # Test case 13 (RIPEMD) - ('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', - 'dd' * 50, - dict(RIPEMD160='b0b105360de759960ab4f35298e116e295d8e7c1'), - 'RFC 2286 #3 (HMAC-RIPEMD)'), - - # Test case 14 (RIPEMD) - ('0102030405060708090a0b0c0d0e0f10111213141516171819', - 'cd' * 50, - dict(RIPEMD160='d5ca862f4d21d5e610e18b4cf1beb97a4365ecf4'), - 'RFC 2286 #4 (HMAC-RIPEMD)'), - - # Test case 15 (RIPEMD) - ('0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c', - xl("Test With Truncation"), - dict(RIPEMD160='7619693978f91d90539ae786500ff3d8e0518e39'), - 'RFC 2286 #5 (HMAC-RIPEMD)'), - - # Test case 16 (RIPEMD) - ('aa' * 80, - xl("Test Using Larger Than Block-Size Key - Hash Key First"), - dict(RIPEMD160='6466ca07ac5eac29e1bd523e5ada7605b791fd8b'), - 'RFC 2286 #6 (HMAC-RIPEMD)'), - - # Test case 17 (RIPEMD) - ('aa' * 80, - xl("Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"), - dict(RIPEMD160='69ea60798d71616cce5fd0871e23754cd75d5a0a'), - 'RFC 2286 #7 (HMAC-RIPEMD)'), - - # From https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/HMAC_SHA3-224.pdf - ( - '000102030405060708090a0b0c0d0e0f' - '101112131415161718191a1b', - xl('Sample message for keylenblocklen'), - dict(SHA3_224='078695eecc227c636ad31d063a15dd05a7e819a66ec6d8de1e193e59'), - 'NIST CSRC Sample #3 (SHA3-224)' - ), - - # From https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/HMAC_SHA3-256.pdf - ( - '000102030405060708090a0b0c0d0e0f'\ - '101112131415161718191a1b1c1d1e1f', - xl('Sample message for keylenblocklen'), - dict(SHA3_256='9bcf2c238e235c3ce88404e813bd2f3a97185ac6f238c63d6229a00b07974258'), - 'NIST CSRC Sample #3 (SHA3-256)' - ), - - # From https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/HMAC_SHA3-384.pdf - ( - '000102030405060708090a0b0c0d0e0f'\ - '101112131415161718191a1b1c1d1e1f' - '202122232425262728292a2b2c2d2e2f', - xl('Sample message for keylenblocklen'), - dict(SHA3_384='e5ae4c739f455279368ebf36d4f5354c95aa184c899d3870e460ebc288ef1f9470053f73f7c6da2a71bcaec38ce7d6ac'), - 'NIST CSRC Sample #3 (SHA3-384)' - ), - - # From https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/HMAC_SHA3-512.pdf - ( - '000102030405060708090a0b0c0d0e0f'\ - '101112131415161718191a1b1c1d1e1f'\ - '202122232425262728292a2b2c2d2e2f'\ - '303132333435363738393a3b3c3d3e3f', - xl('Sample message for keylenblocklen'), - dict(SHA3_512='5f464f5e5b7848e3885e49b2c385f0694985d0e38966242dc4a5fe3fea4b37d46b65ceced5dcf59438dd840bab22269f0ba7febdb9fcf74602a35666b2a32915'), - 'NIST CSRC Sample #3 (SHA3-512)' - ), - -] - - -class HMAC_Module_and_Instance_Test(unittest.TestCase): - """Test the HMAC construction and verify that it does not - matter if you initialize it with a hash module or - with an hash instance. - - See https://bugs.launchpad.net/pycrypto/+bug/1209399 - """ - - def __init__(self, hashmods): - """Initialize the test with a dictionary of hash modules - indexed by their names""" - - unittest.TestCase.__init__(self) - self.hashmods = hashmods - self.description = "" - - def shortDescription(self): - return self.description - - def runTest(self): - key = b"\x90\x91\x92\x93" * 4 - payload = b"\x00" * 100 - - for hashname, hashmod in self.hashmods.items(): - if hashmod is None: - continue - self.description = "Test HMAC in combination with " + hashname - one = HMAC.new(key, payload, hashmod).digest() - two = HMAC.new(key, payload, hashmod.new()).digest() - self.assertEqual(one, two) - - -class HMAC_None(unittest.TestCase): - - def runTest(self): - - key = b"\x04" * 20 - one = HMAC.new(key, b"", SHA1).digest() - two = HMAC.new(key, None, SHA1).digest() - self.assertEqual(one, two) - - -class ByteArrayTests(unittest.TestCase): - - def runTest(self): - - key = b"0" * 16 - data = b"\x00\x01\x02" - - # Data and key can be a bytearray (during initialization) - key_ba = bytearray(key) - data_ba = bytearray(data) - - h1 = HMAC.new(key, data) - h2 = HMAC.new(key_ba, data_ba) - key_ba[:1] = b'\xFF' - data_ba[:1] = b'\xFF' - self.assertEqual(h1.digest(), h2.digest()) - - # Data can be a bytearray (during operation) - key_ba = bytearray(key) - data_ba = bytearray(data) - - h1 = HMAC.new(key) - h2 = HMAC.new(key) - h1.update(data) - h2.update(data_ba) - data_ba[:1] = b'\xFF' - self.assertEqual(h1.digest(), h2.digest()) - - -class MemoryViewTests(unittest.TestCase): - - def runTest(self): - - key = b"0" * 16 - data = b"\x00\x01\x02" - - def get_mv_ro(data): - return memoryview(data) - - def get_mv_rw(data): - return memoryview(bytearray(data)) - - for get_mv in (get_mv_ro, get_mv_rw): - - # Data and key can be a memoryview (during initialization) - key_mv = get_mv(key) - data_mv = get_mv(data) - - h1 = HMAC.new(key, data) - h2 = HMAC.new(key_mv, data_mv) - if not data_mv.readonly: - key_mv[:1] = b'\xFF' - data_mv[:1] = b'\xFF' - self.assertEqual(h1.digest(), h2.digest()) - - # Data can be a memoryview (during operation) - data_mv = get_mv(data) - - h1 = HMAC.new(key) - h2 = HMAC.new(key) - h1.update(data) - h2.update(data_mv) - if not data_mv.readonly: - data_mv[:1] = b'\xFF' - self.assertEqual(h1.digest(), h2.digest()) - - -def get_tests(config={}): - global test_data - import types - from .common import make_mac_tests - - # A test vector contains multiple results, each one for a - # different hash algorithm. - # Here we expand each test vector into multiple ones, - # and add the relevant parameters that will be passed to new() - exp_test_data = [] - for row in test_data: - for modname in row[2].keys(): - t = list(row) - t[2] = row[2][modname] - t.append(dict(digestmod=globals()[modname])) - exp_test_data.append(t) - tests = make_mac_tests(HMAC, "HMAC", exp_test_data) - tests.append(HMAC_Module_and_Instance_Test(hash_modules)) - tests.append(HMAC_None()) - - tests.append(ByteArrayTests()) - tests.append(MemoryViewTests()) - - return tests - - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_KMAC.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_KMAC.py deleted file mode 100644 index 0543a4c..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_KMAC.py +++ /dev/null @@ -1,346 +0,0 @@ -import unittest -from binascii import unhexlify, hexlify - -from Cryptodome.Util.py3compat import tobytes -from Cryptodome.Util.strxor import strxor_c -from Cryptodome.SelfTest.st_common import list_test_cases - -from Cryptodome.Hash import KMAC128, KMAC256 - - -class KMACTest(unittest.TestCase): - - def new(self, *args, **kwargs): - return self.KMAC.new(key=b'X' * (self.minimum_key_bits // 8), *args, **kwargs) - - def test_new_positive(self): - - key = b'X' * 32 - - h = self.new() - for new_func in self.KMAC.new, h.new: - - for dbytes in range(self.minimum_bytes, 128 + 1): - hobj = new_func(key=key, mac_len=dbytes) - self.assertEqual(hobj.digest_size, dbytes) - - digest1 = new_func(key=key, data=b"\x90").digest() - digest2 = new_func(key=key).update(b"\x90").digest() - self.assertEqual(digest1, digest2) - - new_func(data=b"A", key=key, custom=b"g") - - hobj = h.new(key=key) - self.assertEqual(hobj.digest_size, self.default_bytes) - - def test_new_negative(self): - - h = self.new() - for new_func in self.KMAC.new, h.new: - self.assertRaises(ValueError, new_func, key=b'X'*32, - mac_len=0) - self.assertRaises(ValueError, new_func, key=b'X'*32, - mac_len=self.minimum_bytes - 1) - self.assertRaises(TypeError, new_func, - key=u"string") - self.assertRaises(TypeError, new_func, - data=u"string") - - def test_default_digest_size(self): - digest = self.new(data=b'abc').digest() - self.assertEqual(len(digest), self.default_bytes) - - def test_update(self): - pieces = [b"\x0A" * 200, b"\x14" * 300] - h = self.new() - h.update(pieces[0]).update(pieces[1]) - digest = h.digest() - h = self.new() - h.update(pieces[0] + pieces[1]) - self.assertEqual(h.digest(), digest) - - def test_update_negative(self): - h = self.new() - self.assertRaises(TypeError, h.update, u"string") - - def test_digest(self): - h = self.new() - digest = h.digest() - - # hexdigest does not change the state - self.assertEqual(h.digest(), digest) - # digest returns a byte string - self.assertTrue(isinstance(digest, type(b"digest"))) - - def test_update_after_digest(self): - msg = b"rrrrttt" - - # Normally, update() cannot be done after digest() - h = self.new(mac_len=32, data=msg[:4]) - dig1 = h.digest() - self.assertRaises(TypeError, h.update, dig1) - - def test_hex_digest(self): - mac = self.new() - digest = mac.digest() - hexdigest = mac.hexdigest() - - # hexdigest is equivalent to digest - self.assertEqual(hexlify(digest), tobytes(hexdigest)) - # hexdigest does not change the state - self.assertEqual(mac.hexdigest(), hexdigest) - # hexdigest returns a string - self.assertTrue(isinstance(hexdigest, type("digest"))) - - def test_verify(self): - h = self.new() - mac = h.digest() - h.verify(mac) - wrong_mac = strxor_c(mac, 255) - self.assertRaises(ValueError, h.verify, wrong_mac) - - def test_hexverify(self): - h = self.new() - mac = h.hexdigest() - h.hexverify(mac) - self.assertRaises(ValueError, h.hexverify, "4556") - - def test_oid(self): - - oid = "2.16.840.1.101.3.4.2." + self.oid_variant - h = self.new() - self.assertEqual(h.oid, oid) - - def test_bytearray(self): - - key = b'0' * 32 - data = b"\x00\x01\x02" - - # Data and key can be a bytearray (during initialization) - key_ba = bytearray(key) - data_ba = bytearray(data) - - h1 = self.KMAC.new(data=data, key=key) - h2 = self.KMAC.new(data=data_ba, key=key_ba) - key_ba[:1] = b'\xFF' - data_ba[:1] = b'\xFF' - - self.assertEqual(h1.digest(), h2.digest()) - - # Data can be a bytearray (during operation) - data_ba = bytearray(data) - - h1 = self.new() - h2 = self.new() - h1.update(data) - h2.update(data_ba) - data_ba[:1] = b'\xFF' - - self.assertEqual(h1.digest(), h2.digest()) - - def test_memoryview(self): - - key = b'0' * 32 - data = b"\x00\x01\x02" - - def get_mv_ro(data): - return memoryview(data) - - def get_mv_rw(data): - return memoryview(bytearray(data)) - - for get_mv in (get_mv_ro, get_mv_rw): - - # Data and key can be a memoryview (during initialization) - key_mv = get_mv(key) - data_mv = get_mv(data) - - h1 = self.KMAC.new(data=data, key=key) - h2 = self.KMAC.new(data=data_mv, key=key_mv) - if not data_mv.readonly: - data_mv[:1] = b'\xFF' - key_mv[:1] = b'\xFF' - - self.assertEqual(h1.digest(), h2.digest()) - - # Data can be a memoryview (during operation) - data_mv = get_mv(data) - - h1 = self.new() - h2 = self.new() - h1.update(data) - h2.update(data_mv) - if not data_mv.readonly: - data_mv[:1] = b'\xFF' - - self.assertEqual(h1.digest(), h2.digest()) - - -class KMAC128Test(KMACTest): - - KMAC = KMAC128 - - minimum_key_bits = 128 - - minimum_bytes = 8 - default_bytes = 64 - - oid_variant = "19" - - -class KMAC256Test(KMACTest): - - KMAC = KMAC256 - - minimum_key_bits = 256 - - minimum_bytes = 8 - default_bytes = 64 - - oid_variant = "20" - - -class NISTExampleTestVectors(unittest.TestCase): - - # https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/KMAC_samples.pdf - test_data = [ - ( - "40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F" - "50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F", - "00 01 02 03", - "", - "E5 78 0B 0D 3E A6 F7 D3 A4 29 C5 70 6A A4 3A 00" - "FA DB D7 D4 96 28 83 9E 31 87 24 3F 45 6E E1 4E", - "Sample #1 NIST", - KMAC128 - ), - ( - "40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F" - "50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F", - "00 01 02 03", - "My Tagged Application", - "3B 1F BA 96 3C D8 B0 B5 9E 8C 1A 6D 71 88 8B 71" - "43 65 1A F8 BA 0A 70 70 C0 97 9E 28 11 32 4A A5", - "Sample #2 NIST", - KMAC128 - ), - ( - "40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F" - "50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F", - "00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F" - "10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F" - "20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F" - "30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F" - "40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F" - "50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F" - "60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F" - "70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F" - "80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F" - "90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F" - "A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF" - "B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF" - "C0 C1 C2 C3 C4 C5 C6 C7", - "My Tagged Application", - "1F 5B 4E 6C CA 02 20 9E 0D CB 5C A6 35 B8 9A 15" - "E2 71 EC C7 60 07 1D FD 80 5F AA 38 F9 72 92 30", - "Sample #3 NIST", - KMAC128 - ), - ( - "40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F" - "50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F", - "00 01 02 03", - "My Tagged Application", - "20 C5 70 C3 13 46 F7 03 C9 AC 36 C6 1C 03 CB 64" - "C3 97 0D 0C FC 78 7E 9B 79 59 9D 27 3A 68 D2 F7" - "F6 9D 4C C3 DE 9D 10 4A 35 16 89 F2 7C F6 F5 95" - "1F 01 03 F3 3F 4F 24 87 10 24 D9 C2 77 73 A8 DD", - "Sample #4 NIST", - KMAC256 - ), - ( - "40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F" - "50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F", - "00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F" - "10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F" - "20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F" - "30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F" - "40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F" - "50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F" - "60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F" - "70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F" - "80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F" - "90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F" - "A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF" - "B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF" - "C0 C1 C2 C3 C4 C5 C6 C7", - "", - "75 35 8C F3 9E 41 49 4E 94 97 07 92 7C EE 0A F2" - "0A 3F F5 53 90 4C 86 B0 8F 21 CC 41 4B CF D6 91" - "58 9D 27 CF 5E 15 36 9C BB FF 8B 9A 4C 2E B1 78" - "00 85 5D 02 35 FF 63 5D A8 25 33 EC 6B 75 9B 69", - "Sample #5 NIST", - KMAC256 - ), - ( - "40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F" - "50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F", - "00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F" - "10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F" - "20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F" - "30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F" - "40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F" - "50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F" - "60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F" - "70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F" - "80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F" - "90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F" - "A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF" - "B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF" - "C0 C1 C2 C3 C4 C5 C6 C7", - "My Tagged Application", - "B5 86 18 F7 1F 92 E1 D5 6C 1B 8C 55 DD D7 CD 18" - "8B 97 B4 CA 4D 99 83 1E B2 69 9A 83 7D A2 E4 D9" - "70 FB AC FD E5 00 33 AE A5 85 F1 A2 70 85 10 C3" - "2D 07 88 08 01 BD 18 28 98 FE 47 68 76 FC 89 65", - "Sample #6 NIST", - KMAC256 - ), - ] - - def setUp(self): - td = [] - for key, data, custom, mac, text, module in self.test_data: - ni = ( - unhexlify(key.replace(" ", "")), - unhexlify(data.replace(" ", "")), - custom.encode(), - unhexlify(mac.replace(" ", "")), - text, - module - ) - td.append(ni) - self.test_data = td - - def runTest(self): - - for key, data, custom, mac, text, module in self.test_data: - h = module.new(data=data, key=key, custom=custom, mac_len=len(mac)) - mac_tag = h.digest() - self.assertEqual(mac_tag, mac, msg=text) - - -def get_tests(config={}): - tests = [] - - tests += list_test_cases(KMAC128Test) - tests += list_test_cases(KMAC256Test) - tests.append(NISTExampleTestVectors()) - - return tests - - -if __name__ == '__main__': - def suite(): - return unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_KangarooTwelve.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_KangarooTwelve.py deleted file mode 100644 index c9ad363..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_KangarooTwelve.py +++ /dev/null @@ -1,367 +0,0 @@ -# =================================================================== -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -"""Self-test suite for Cryptodome.Hash.KangarooTwelve""" - -import unittest -from binascii import unhexlify - -from Cryptodome.SelfTest.st_common import list_test_cases - -from Cryptodome.Hash import KangarooTwelve as K12 -from Cryptodome.Util.py3compat import b, bchr - - -class KangarooTwelveTest(unittest.TestCase): - - def test_length_encode(self): - self.assertEqual(K12._length_encode(0), b'\x00') - self.assertEqual(K12._length_encode(12), b'\x0C\x01') - self.assertEqual(K12._length_encode(65538), b'\x01\x00\x02\x03') - - def test_new_positive(self): - - xof1 = K12.new() - xof2 = K12.new(data=b("90")) - xof3 = K12.new().update(b("90")) - - self.assertNotEqual(xof1.read(10), xof2.read(10)) - xof3.read(10) - self.assertEqual(xof2.read(10), xof3.read(10)) - - xof1 = K12.new() - ref = xof1.read(10) - xof2 = K12.new(custom=b("")) - xof3 = K12.new(custom=b("foo")) - - self.assertEqual(ref, xof2.read(10)) - self.assertNotEqual(ref, xof3.read(10)) - - xof1 = K12.new(custom=b("foo")) - xof2 = K12.new(custom=b("foo"), data=b("90")) - xof3 = K12.new(custom=b("foo")).update(b("90")) - - self.assertNotEqual(xof1.read(10), xof2.read(10)) - xof3.read(10) - self.assertEqual(xof2.read(10), xof3.read(10)) - - def test_update(self): - pieces = [bchr(10) * 200, bchr(20) * 300] - h = K12.new() - h.update(pieces[0]).update(pieces[1]) - digest = h.read(10) - h = K12.new() - h.update(pieces[0] + pieces[1]) - self.assertEqual(h.read(10), digest) - - def test_update_negative(self): - h = K12.new() - self.assertRaises(TypeError, h.update, u"string") - - def test_digest(self): - h = K12.new() - digest = h.read(90) - - # read returns a byte string of the right length - self.assertTrue(isinstance(digest, type(b("digest")))) - self.assertEqual(len(digest), 90) - - def test_update_after_read(self): - mac = K12.new() - mac.update(b("rrrr")) - mac.read(90) - self.assertRaises(TypeError, mac.update, b("ttt")) - - -def txt2bin(txt): - clean = txt.replace(" ", "").replace("\n", "").replace("\r", "") - return unhexlify(clean) - - -def ptn(n): - res = bytearray(n) - pattern = b"".join([bchr(x) for x in range(0, 0xFB)]) - for base in range(0, n - 0xFB, 0xFB): - res[base:base + 0xFB] = pattern - remain = n % 0xFB - if remain: - base = (n // 0xFB) * 0xFB - res[base:] = pattern[:remain] - assert(len(res) == n) - return res - - -def chunked(source, size): - for i in range(0, len(source), size): - yield source[i:i+size] - - -class KangarooTwelveTV(unittest.TestCase): - - # https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KangarooTwelve.txt - - def test_zero_1(self): - tv = """1A C2 D4 50 FC 3B 42 05 D1 9D A7 BF CA 1B 37 51 - 3C 08 03 57 7A C7 16 7F 06 FE 2C E1 F0 EF 39 E5""" - - btv = txt2bin(tv) - res = K12.new().read(32) - self.assertEqual(res, btv) - - def test_zero_2(self): - tv = """1A C2 D4 50 FC 3B 42 05 D1 9D A7 BF CA 1B 37 51 - 3C 08 03 57 7A C7 16 7F 06 FE 2C E1 F0 EF 39 E5 - 42 69 C0 56 B8 C8 2E 48 27 60 38 B6 D2 92 96 6C - C0 7A 3D 46 45 27 2E 31 FF 38 50 81 39 EB 0A 71""" - - btv = txt2bin(tv) - res = K12.new().read(64) - self.assertEqual(res, btv) - - def test_zero_3(self): - tv = """E8 DC 56 36 42 F7 22 8C 84 68 4C 89 84 05 D3 A8 - 34 79 91 58 C0 79 B1 28 80 27 7A 1D 28 E2 FF 6D""" - - btv = txt2bin(tv) - res = K12.new().read(10032) - self.assertEqual(res[-32:], btv) - - def test_ptn_1(self): - tv = """2B DA 92 45 0E 8B 14 7F 8A 7C B6 29 E7 84 A0 58 - EF CA 7C F7 D8 21 8E 02 D3 45 DF AA 65 24 4A 1F""" - - btv = txt2bin(tv) - res = K12.new(data=ptn(1)).read(32) - self.assertEqual(res, btv) - - def test_ptn_17(self): - tv = """6B F7 5F A2 23 91 98 DB 47 72 E3 64 78 F8 E1 9B - 0F 37 12 05 F6 A9 A9 3A 27 3F 51 DF 37 12 28 88""" - - btv = txt2bin(tv) - res = K12.new(data=ptn(17)).read(32) - self.assertEqual(res, btv) - - def test_ptn_17_2(self): - tv = """0C 31 5E BC DE DB F6 14 26 DE 7D CF 8F B7 25 D1 - E7 46 75 D7 F5 32 7A 50 67 F3 67 B1 08 EC B6 7C""" - - btv = txt2bin(tv) - res = K12.new(data=ptn(17**2)).read(32) - self.assertEqual(res, btv) - - def test_ptn_17_3(self): - tv = """CB 55 2E 2E C7 7D 99 10 70 1D 57 8B 45 7D DF 77 - 2C 12 E3 22 E4 EE 7F E4 17 F9 2C 75 8F 0D 59 D0""" - - btv = txt2bin(tv) - res = K12.new(data=ptn(17**3)).read(32) - self.assertEqual(res, btv) - - def test_ptn_17_4(self): - tv = """87 01 04 5E 22 20 53 45 FF 4D DA 05 55 5C BB 5C - 3A F1 A7 71 C2 B8 9B AE F3 7D B4 3D 99 98 B9 FE""" - - btv = txt2bin(tv) - data = ptn(17**4) - - # All at once - res = K12.new(data=data).read(32) - self.assertEqual(res, btv) - - # Byte by byte - k12 = K12.new() - for x in data: - k12.update(bchr(x)) - res = k12.read(32) - self.assertEqual(res, btv) - - # Chunks of various prime sizes - for chunk_size in (13, 17, 19, 23, 31): - k12 = K12.new() - for x in chunked(data, chunk_size): - k12.update(x) - res = k12.read(32) - self.assertEqual(res, btv) - - def test_ptn_17_5(self): - tv = """84 4D 61 09 33 B1 B9 96 3C BD EB 5A E3 B6 B0 5C - C7 CB D6 7C EE DF 88 3E B6 78 A0 A8 E0 37 16 82""" - - btv = txt2bin(tv) - data = ptn(17**5) - - # All at once - res = K12.new(data=data).read(32) - self.assertEqual(res, btv) - - # Chunks - k12 = K12.new() - for chunk in chunked(data, 8192): - k12.update(chunk) - res = k12.read(32) - self.assertEqual(res, btv) - - def test_ptn_17_6(self): - tv = """3C 39 07 82 A8 A4 E8 9F A6 36 7F 72 FE AA F1 32 - 55 C8 D9 58 78 48 1D 3C D8 CE 85 F5 8E 88 0A F8""" - - btv = txt2bin(tv) - data = ptn(17**6) - - # All at once - res = K12.new(data=data).read(32) - self.assertEqual(res, btv) - - def test_ptn_c_1(self): - tv = """FA B6 58 DB 63 E9 4A 24 61 88 BF 7A F6 9A 13 30 - 45 F4 6E E9 84 C5 6E 3C 33 28 CA AF 1A A1 A5 83""" - - btv = txt2bin(tv) - custom = ptn(1) - - # All at once - res = K12.new(custom=custom).read(32) - self.assertEqual(res, btv) - - def test_ptn_c_41(self): - tv = """D8 48 C5 06 8C ED 73 6F 44 62 15 9B 98 67 FD 4C - 20 B8 08 AC C3 D5 BC 48 E0 B0 6B A0 A3 76 2E C4""" - - btv = txt2bin(tv) - custom = ptn(41) - - # All at once - res = K12.new(data=b'\xFF', custom=custom).read(32) - self.assertEqual(res, btv) - - def test_ptn_c_41_2(self): - tv = """C3 89 E5 00 9A E5 71 20 85 4C 2E 8C 64 67 0A C0 - 13 58 CF 4C 1B AF 89 44 7A 72 42 34 DC 7C ED 74""" - - btv = txt2bin(tv) - custom = ptn(41**2) - - # All at once - res = K12.new(data=b'\xFF' * 3, custom=custom).read(32) - self.assertEqual(res, btv) - - def test_ptn_c_41_3(self): - tv = """75 D2 F8 6A 2E 64 45 66 72 6B 4F BC FC 56 57 B9 - DB CF 07 0C 7B 0D CA 06 45 0A B2 91 D7 44 3B CF""" - - btv = txt2bin(tv) - custom = ptn(41**3) - - # All at once - res = K12.new(data=b'\xFF' * 7, custom=custom).read(32) - self.assertEqual(res, btv) - - # https://datatracker.ietf.org/doc/draft-irtf-cfrg-kangarootwelve/ - - def test_ptn_8191(self): - tv = """1B 57 76 36 F7 23 64 3E 99 0C C7 D6 A6 59 83 74 - 36 FD 6A 10 36 26 60 0E B8 30 1C D1 DB E5 53 D6""" - - btv = txt2bin(tv) - - # All at once - res = K12.new(data=ptn(8191)).read(32) - self.assertEqual(res, btv) - - def test_ptn_8192(self): - tv = """48 F2 56 F6 77 2F 9E DF B6 A8 B6 61 EC 92 DC 93 - B9 5E BD 05 A0 8A 17 B3 9A E3 49 08 70 C9 26 C3""" - - btv = txt2bin(tv) - - # All at once - res = K12.new(data=ptn(8192)).read(32) - self.assertEqual(res, btv) - - def test_ptn_8192_8189(self): - tv = """3E D1 2F 70 FB 05 DD B5 86 89 51 0A B3 E4 D2 3C - 6C 60 33 84 9A A0 1E 1D 8C 22 0A 29 7F ED CD 0B""" - - btv = txt2bin(tv) - - # All at once - res = K12.new(data=ptn(8192), custom=ptn(8189)).read(32) - self.assertEqual(res, btv) - - def test_ptn_8192_8190(self): - tv = """6A 7C 1B 6A 5C D0 D8 C9 CA 94 3A 4A 21 6C C6 46 - 04 55 9A 2E A4 5F 78 57 0A 15 25 3D 67 BA 00 AE""" - - btv = txt2bin(tv) - - # All at once - res = K12.new(data=ptn(8192), custom=ptn(8190)).read(32) - self.assertEqual(res, btv) - - ### - - def test_1(self): - tv = "fd608f91d81904a9916e78a18f65c157a78d63f93d8f6367db0524526a5ea2bb" - - btv = txt2bin(tv) - res = K12.new(data=b'', custom=ptn(100)).read(32) - self.assertEqual(res, btv) - - def test_2(self): - tv4 = "5a4ec9a649f81916d4ce1553492962f7868abf8dd1ceb2f0cb3682ea95cda6a6" - tv3 = "441688fe4fe4ae9425eb3105eb445eb2b3a6f67b66eff8e74ebfbc49371f6d4c" - tv2 = "17269a57759af0214c84a0fd9bc851f4d95f80554cfed4e7da8a6ee1ff080131" - tv1 = "33826990c09dc712ba7224f0d9be319e2720de95a4c1afbd2211507dae1c703a" - tv0 = "9f4d3aba908ddc096e4d3a71da954f917b9752f05052b9d26d916a6fbc75bf3e" - - res = K12.new(data=b'A' * (8192 - 4), custom=b'B').read(32) - self.assertEqual(res, txt2bin(tv4)) - - res = K12.new(data=b'A' * (8192 - 3), custom=b'B').read(32) - self.assertEqual(res, txt2bin(tv3)) - - res = K12.new(data=b'A' * (8192 - 2), custom=b'B').read(32) - self.assertEqual(res, txt2bin(tv2)) - - res = K12.new(data=b'A' * (8192 - 1), custom=b'B').read(32) - self.assertEqual(res, txt2bin(tv1)) - - res = K12.new(data=b'A' * (8192 - 0), custom=b'B').read(32) - self.assertEqual(res, txt2bin(tv0)) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(KangarooTwelveTest) - tests += list_test_cases(KangarooTwelveTV) - return tests - - -if __name__ == '__main__': - def suite(): - return unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_MD2.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_MD2.py deleted file mode 100644 index beae38a..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_MD2.py +++ /dev/null @@ -1,62 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Hash/MD2.py: Self-test for the MD2 hash function -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Hash.MD2""" - -from Cryptodome.Util.py3compat import * - -# This is a list of (expected_result, input[, description]) tuples. -test_data = [ - # Test vectors from RFC 1319 - ('8350e5a3e24c153df2275c9f80692773', '', "'' (empty string)"), - ('32ec01ec4a6dac72c0ab96fb34c0b5d1', 'a'), - ('da853b0d3f88d99b30283a69e6ded6bb', 'abc'), - ('ab4f496bfb2a530b219ff33031fe06b0', 'message digest'), - - ('4e8ddff3650292ab5a4108c3aa47940b', 'abcdefghijklmnopqrstuvwxyz', - 'a-z'), - - ('da33def2a42df13975352846c30338cd', - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', - 'A-Z, a-z, 0-9'), - - ('d5976f79d83d3a0dc9806c3c66f3efd8', - '1234567890123456789012345678901234567890123456' - + '7890123456789012345678901234567890', - "'1234567890' * 8"), -] - -def get_tests(config={}): - from Cryptodome.Hash import MD2 - from .common import make_hash_tests - return make_hash_tests(MD2, "MD2", test_data, - digest_size=16, - oid="1.2.840.113549.2.2") - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_MD4.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_MD4.py deleted file mode 100644 index 41de977..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_MD4.py +++ /dev/null @@ -1,64 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Hash/MD4.py: Self-test for the MD4 hash function -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Hash.MD4""" - -__revision__ = "$Id$" - -from Cryptodome.Util.py3compat import * - -# This is a list of (expected_result, input[, description]) tuples. -test_data = [ - # Test vectors from RFC 1320 - ('31d6cfe0d16ae931b73c59d7e0c089c0', '', "'' (empty string)"), - ('bde52cb31de33e46245e05fbdbd6fb24', 'a'), - ('a448017aaf21d8525fc10ae87aa6729d', 'abc'), - ('d9130a8164549fe818874806e1c7014b', 'message digest'), - - ('d79e1c308aa5bbcdeea8ed63df412da9', 'abcdefghijklmnopqrstuvwxyz', - 'a-z'), - - ('043f8582f241db351ce627e153e7f0e4', - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', - 'A-Z, a-z, 0-9'), - - ('e33b4ddc9c38f2199c3e7b164fcc0536', - '1234567890123456789012345678901234567890123456' - + '7890123456789012345678901234567890', - "'1234567890' * 8"), -] - -def get_tests(config={}): - from Cryptodome.Hash import MD4 - from .common import make_hash_tests - return make_hash_tests(MD4, "MD4", test_data, - digest_size=16, - oid="1.2.840.113549.2.4") - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_MD5.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_MD5.py deleted file mode 100644 index 3f7a005..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_MD5.py +++ /dev/null @@ -1,94 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Hash/MD5.py: Self-test for the MD5 hash function -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Hash.MD5""" - -from Cryptodome.Util.py3compat import * -from Cryptodome.Hash import MD5 -from binascii import unhexlify -import unittest -from Cryptodome.SelfTest.st_common import list_test_cases - - -# This is a list of (expected_result, input[, description]) tuples. -test_data = [ - # Test vectors from RFC 1321 - ('d41d8cd98f00b204e9800998ecf8427e', '', "'' (empty string)"), - ('0cc175b9c0f1b6a831c399e269772661', 'a'), - ('900150983cd24fb0d6963f7d28e17f72', 'abc'), - ('f96b697d7cb7938d525a2f31aaf161d0', 'message digest'), - - ('c3fcd3d76192e4007dfb496cca67e13b', 'abcdefghijklmnopqrstuvwxyz', - 'a-z'), - - ('d174ab98d277d9f5a5611c2c9f419d9f', - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', - 'A-Z, a-z, 0-9'), - - ('57edf4a22be3c955ac49da2e2107b67a', - '1234567890123456789012345678901234567890123456' - + '7890123456789012345678901234567890', - "'1234567890' * 8"), - - # https://www.cosic.esat.kuleuven.be/nessie/testvectors/hash/md5/Md5-128.unverified.test-vectors - ('57EDF4A22BE3C955AC49DA2E2107B67A', '1234567890' * 8, 'Set 1, vector #7'), - ('7707D6AE4E027C70EEA2A935C2296F21', 'a'*1000000, 'Set 1, vector #8'), -] - - -class Md5IterTest(unittest.TestCase): - - def runTest(self): - message = b("\x00") * 16 - result1 = "4AE71336E44BF9BF79D2752E234818A5".lower() - result2 = "1A83F51285E4D89403D00C46EF8508FE".lower() - - h = MD5.new(message) - message = h.digest() - self.assertEqual(h.hexdigest(), result1) - - for _ in range(99999): - h = MD5.new(message) - message = h.digest() - - self.assertEqual(h.hexdigest(), result2) - - -def get_tests(config={}): - from .common import make_hash_tests - - tests = make_hash_tests(MD5, "MD5", test_data, - digest_size=16, - oid="1.2.840.113549.2.5") - if config.get('slow_tests'): - tests += [ Md5IterTest() ] - return tests - - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_Poly1305.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_Poly1305.py deleted file mode 100644 index 19cacb4..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_Poly1305.py +++ /dev/null @@ -1,542 +0,0 @@ -# -# SelfTest/Hash/test_Poly1305.py: Self-test for the Poly1305 module -# -# =================================================================== -# -# Copyright (c) 2018, Helder Eijs -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -"""Self-test suite for Cryptodome.Hash._Poly1305""" - -import json -import unittest -from binascii import unhexlify, hexlify - -from .common import make_mac_tests -from Cryptodome.SelfTest.st_common import list_test_cases - -from Cryptodome.Hash import Poly1305 -from Cryptodome.Cipher import AES, ChaCha20 - -from Cryptodome.Util.py3compat import tobytes -from Cryptodome.Util.strxor import strxor_c - -# This is a list of (r+s keypair, data, result, description, keywords) tuples. -test_data_basic = [ - ( - "85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b", - hexlify(b"Cryptographic Forum Research Group").decode(), - "a8061dc1305136c6c22b8baf0c0127a9", - "RFC7539" - ), - ( - "746869732069732033322d62797465206b657920666f7220506f6c7931333035", - "0000000000000000000000000000000000000000000000000000000000000000", - "49ec78090e481ec6c26b33b91ccc0307", - "https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-00#section-7 A", - ), - ( - "746869732069732033322d62797465206b657920666f7220506f6c7931333035", - "48656c6c6f20776f726c6421", - "a6f745008f81c916a20dcc74eef2b2f0", - "https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-00#section-7 B", - ), - ( - "746869732069732033322d62797465206b657920666f7220506f6c7931333035", - "", - "6b657920666f7220506f6c7931333035", - "Generated with pure Python", - ), - ( - "746869732069732033322d62797465206b657920666f7220506f6c7931333035", - "FF", - "f7e4e0ef4c46d106219da3d1bdaeb3ff", - "Generated with pure Python", - ), - ( - "746869732069732033322d62797465206b657920666f7220506f6c7931333035", - "FF00", - "7471eceeb22988fc936da1d6e838b70e", - "Generated with pure Python", - ), - ( - "746869732069732033322d62797465206b657920666f7220506f6c7931333035", - "AA" * 17, - "32590bc07cb2afaccca3f67f122975fe", - "Generated with pure Python", - ), - ( - "00" * 32, - "00" * 64, - "00" * 16, - "RFC7539 A.3 #1", - ), - ( - "0000000000000000000000000000000036e5f6b5c5e06070f0efca96227a863e", - hexlify( - b"Any submission t" - b"o the IETF inten" - b"ded by the Contr" - b"ibutor for publi" - b"cation as all or" - b" part of an IETF" - b" Internet-Draft " - b"or RFC and any s" - b"tatement made wi" - b"thin the context" - b" of an IETF acti" - b"vity is consider" - b"ed an \"IETF Cont" - b"ribution\". Such " - b"statements inclu" - b"de oral statemen" - b"ts in IETF sessi" - b"ons, as well as " - b"written and elec" - b"tronic communica" - b"tions made at an" - b"y time or place," - b" which are addre" - b"ssed to").decode(), - "36e5f6b5c5e06070f0efca96227a863e", - "RFC7539 A.3 #2", - ), - ( - "36e5f6b5c5e06070f0efca96227a863e00000000000000000000000000000000", - hexlify( - b"Any submission t" - b"o the IETF inten" - b"ded by the Contr" - b"ibutor for publi" - b"cation as all or" - b" part of an IETF" - b" Internet-Draft " - b"or RFC and any s" - b"tatement made wi" - b"thin the context" - b" of an IETF acti" - b"vity is consider" - b"ed an \"IETF Cont" - b"ribution\". Such " - b"statements inclu" - b"de oral statemen" - b"ts in IETF sessi" - b"ons, as well as " - b"written and elec" - b"tronic communica" - b"tions made at an" - b"y time or place," - b" which are addre" - b"ssed to").decode(), - "f3477e7cd95417af89a6b8794c310cf0", - "RFC7539 A.3 #3", - ), - ( - "1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0", - "2754776173206272696c6c69672c2061" - "6e642074686520736c6974687920746f" - "7665730a446964206779726520616e64" - "2067696d626c6520696e207468652077" - "6162653a0a416c6c206d696d73792077" - "6572652074686520626f726f676f7665" - "732c0a416e6420746865206d6f6d6520" - "7261746873206f757467726162652e", - "4541669a7eaaee61e708dc7cbcc5eb62", - "RFC7539 A.3 #4", - ), - ( - "02" + "00" * 31, - "FF" * 16, - "03" + "00" * 15, - "RFC7539 A.3 #5", - ), - ( - "02" + "00" * 15 + "FF" * 16, - "02" + "00" * 15, - "03" + "00" * 15, - "RFC7539 A.3 #6", - ), - ( - "01" + "00" * 31, - "FF" * 16 + "F0" + "FF" * 15 + "11" + "00" * 15, - "05" + "00" * 15, - "RFC7539 A.3 #7", - ), - ( - "01" + "00" * 31, - "FF" * 16 + "FB" + "FE" * 15 + "01" * 16, - "00" * 16, - "RFC7539 A.3 #8", - ), - ( - "02" + "00" * 31, - "FD" + "FF" * 15, - "FA" + "FF" * 15, - "RFC7539 A.3 #9", - ), - ( - "01 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00" - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", - "E3 35 94 D7 50 5E 43 B9 00 00 00 00 00 00 00 00" - "33 94 D7 50 5E 43 79 CD 01 00 00 00 00 00 00 00" - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" - "01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", - "14 00 00 00 00 00 00 00 55 00 00 00 00 00 00 00", - "RFC7539 A.3 #10", - ), - ( - "01 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00" - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", - "E3 35 94 D7 50 5E 43 B9 00 00 00 00 00 00 00 00" - "33 94 D7 50 5E 43 79 CD 01 00 00 00 00 00 00 00" - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", - "13" + "00" * 15, - "RFC7539 A.3 #11", - ), -] - -# This is a list of (key(k+r), data, result, description, keywords) tuples. -test_data_aes = [ - ( - "ec074c835580741701425b623235add6851fc40c3467ac0be05cc20404f3f700", - "f3f6", - "f4c633c3044fc145f84f335cb81953de", - "http://cr.yp.to/mac/poly1305-20050329.pdf", - { 'cipher':AES, 'nonce':unhexlify("fb447350c4e868c52ac3275cf9d4327e") } - ), - ( - "75deaa25c09f208e1dc4ce6b5cad3fbfa0f3080000f46400d0c7e9076c834403", - "", - "dd3fab2251f11ac759f0887129cc2ee7", - "http://cr.yp.to/mac/poly1305-20050329.pdf", - { 'cipher':AES, 'nonce':unhexlify("61ee09218d29b0aaed7e154a2c5509cc") } - ), - ( - "6acb5f61a7176dd320c5c1eb2edcdc7448443d0bb0d21109c89a100b5ce2c208", - "663cea190ffb83d89593f3f476b6bc24" - "d7e679107ea26adb8caf6652d0656136", - "0ee1c16bb73f0f4fd19881753c01cdbe", - "http://cr.yp.to/mac/poly1305-20050329.pdf", - { 'cipher':AES, 'nonce':unhexlify("ae212a55399729595dea458bc621ff0e") } - ), - ( - "e1a5668a4d5b66a5f68cc5424ed5982d12976a08c4426d0ce8a82407c4f48207", - "ab0812724a7f1e342742cbed374d94d1" - "36c6b8795d45b3819830f2c04491faf0" - "990c62e48b8018b2c3e4a0fa3134cb67" - "fa83e158c994d961c4cb21095c1bf9", - "5154ad0d2cb26e01274fc51148491f1b", - "http://cr.yp.to/mac/poly1305-20050329.pdf", - { 'cipher':AES, 'nonce':unhexlify("9ae831e743978d3a23527c7128149e3a") } - ), -] - -test_data_chacha20 = [ - ( - "00" * 32, - "FF" * 15, - "13cc5bbadc36b03a5163928f0bcb65aa", - "RFC7539 A.4 #1", - { 'cipher':ChaCha20, 'nonce':unhexlify("00" * 12) } - ), - ( - "00" * 31 + "01", - "FF" * 15, - "0baf33c1d6df211bdd50a6767e98e00a", - "RFC7539 A.4 #2", - { 'cipher':ChaCha20, 'nonce':unhexlify("00" * 11 + "02") } - ), - ( - "1c 92 40 a5 eb 55 d3 8a f3 33 88 86 04 f6 b5 f0" - "47 39 17 c1 40 2b 80 09 9d ca 5c bc 20 70 75 c0", - "FF" * 15, - "e8b4c6db226cd8939e65e02eebf834ce", - "RFC7539 A.4 #3", - { 'cipher':ChaCha20, 'nonce':unhexlify("00" * 11 + "02") } - ), - ( - "1c 92 40 a5 eb 55 d3 8a f3 33 88 86 04 f6 b5 f0" - "47 39 17 c1 40 2b 80 09 9d ca 5c bc 20 70 75 c0", - "f3 33 88 86 00 00 00 00 00 00 4e 91 00 00 00 00" - "64 a0 86 15 75 86 1a f4 60 f0 62 c7 9b e6 43 bd" - "5e 80 5c fd 34 5c f3 89 f1 08 67 0a c7 6c 8c b2" - "4c 6c fc 18 75 5d 43 ee a0 9e e9 4e 38 2d 26 b0" - "bd b7 b7 3c 32 1b 01 00 d4 f0 3b 7f 35 58 94 cf" - "33 2f 83 0e 71 0b 97 ce 98 c8 a8 4a bd 0b 94 81" - "14 ad 17 6e 00 8d 33 bd 60 f9 82 b1 ff 37 c8 55" - "97 97 a0 6e f4 f0 ef 61 c1 86 32 4e 2b 35 06 38" - "36 06 90 7b 6a 7c 02 b0 f9 f6 15 7b 53 c8 67 e4" - "b9 16 6c 76 7b 80 4d 46 a5 9b 52 16 cd e7 a4 e9" - "90 40 c5 a4 04 33 22 5e e2 82 a1 b0 a0 6c 52 3e" - "af 45 34 d7 f8 3f a1 15 5b 00 47 71 8c bc 54 6a" - "0d 07 2b 04 b3 56 4e ea 1b 42 22 73 f5 48 27 1a" - "0b b2 31 60 53 fa 76 99 19 55 eb d6 31 59 43 4e" - "ce bb 4e 46 6d ae 5a 10 73 a6 72 76 27 09 7a 10" - "49 e6 17 d9 1d 36 10 94 fa 68 f0 ff 77 98 71 30" - "30 5b ea ba 2e da 04 df 99 7b 71 4d 6c 6f 2c 29" - "a6 ad 5c b4 02 2b 02 70 9b 00 00 00 00 00 00 00" - "0c 00 00 00 00 00 00 00 09 01 00 00 00 00 00 00", - "ee ad 9d 67 89 0c bb 22 39 23 36 fe a1 85 1f 38", - "RFC7539 A.5", - { 'cipher':ChaCha20, 'nonce':unhexlify("000000000102030405060708") } - ), -] - - -class Poly1305Test_AES(unittest.TestCase): - - key = b'\x11' * 32 - - def test_new_positive(self): - - data = b'r' * 100 - - h1 = Poly1305.new(key=self.key, cipher=AES) - self.assertEqual(h1.digest_size, 16) - self.assertEqual(len(h1.nonce), 16) - d1 = h1.update(data).digest() - self.assertEqual(len(d1), 16) - - h2 = Poly1305.new(key=self.key, nonce=h1.nonce, data=data, cipher=AES) - d2 = h2.digest() - self.assertEqual(h1.nonce, h2.nonce) - self.assertEqual(d1, d2) - - def test_new_negative(self): - from Cryptodome.Cipher import DES3 - - self.assertRaises(ValueError, Poly1305.new, key=self.key[:31], cipher=AES) - self.assertRaises(ValueError, Poly1305.new, key=self.key, cipher=DES3) - self.assertRaises(ValueError, Poly1305.new, key=self.key, nonce=b'1' * 15, cipher=AES) - self.assertRaises(TypeError, Poly1305.new, key=u"2" * 32, cipher=AES) - self.assertRaises(TypeError, Poly1305.new, key=self.key, data=u"2" * 100, cipher=AES) - - def test_update(self): - pieces = [b"\x0A" * 200, b"\x14" * 300] - h1 = Poly1305.new(key=self.key, cipher=AES) - h1.update(pieces[0]).update(pieces[1]) - d1 = h1.digest() - - h2 = Poly1305.new(key=self.key, cipher=AES, nonce=h1.nonce) - h2.update(pieces[0] + pieces[1]) - d2 = h2.digest() - self.assertEqual(d1, d2) - - def test_update_negative(self): - h = Poly1305.new(key=self.key, cipher=AES) - self.assertRaises(TypeError, h.update, u"string") - - def test_digest(self): - h = Poly1305.new(key=self.key, cipher=AES) - digest = h.digest() - - # hexdigest does not change the state - self.assertEqual(h.digest(), digest) - # digest returns a byte string - self.assertTrue(isinstance(digest, type(b"digest"))) - - def test_update_after_digest(self): - msg=b"rrrrttt" - - # Normally, update() cannot be done after digest() - h = Poly1305.new(key=self.key, data=msg[:4], cipher=AES) - h.digest() - self.assertRaises(TypeError, h.update, msg[4:]) - - def test_hex_digest(self): - mac = Poly1305.new(key=self.key, cipher=AES) - digest = mac.digest() - hexdigest = mac.hexdigest() - - # hexdigest is equivalent to digest - self.assertEqual(hexlify(digest), tobytes(hexdigest)) - # hexdigest does not change the state - self.assertEqual(mac.hexdigest(), hexdigest) - # hexdigest returns a string - self.assertTrue(isinstance(hexdigest, type("digest"))) - - def test_verify(self): - h = Poly1305.new(key=self.key, cipher=AES) - mac = h.digest() - h.verify(mac) - wrong_mac = strxor_c(mac, 255) - self.assertRaises(ValueError, h.verify, wrong_mac) - - def test_hexverify(self): - h = Poly1305.new(key=self.key, cipher=AES) - mac = h.hexdigest() - h.hexverify(mac) - self.assertRaises(ValueError, h.hexverify, "4556") - - def test_bytearray(self): - - data = b"\x00\x01\x02" - h0 = Poly1305.new(key=self.key, data=data, cipher=AES) - d_ref = h0.digest() - - # Data and key can be a bytearray (during initialization) - key_ba = bytearray(self.key) - data_ba = bytearray(data) - - h1 = Poly1305.new(key=self.key, data=data, cipher=AES, nonce=h0.nonce) - h2 = Poly1305.new(key=key_ba, data=data_ba, cipher=AES, nonce=h0.nonce) - key_ba[:1] = b'\xFF' - data_ba[:1] = b'\xEE' - - self.assertEqual(h1.digest(), d_ref) - self.assertEqual(h2.digest(), d_ref) - - # Data can be a bytearray (during operation) - data_ba = bytearray(data) - - h1 = Poly1305.new(key=self.key, cipher=AES) - h2 = Poly1305.new(key=self.key, cipher=AES, nonce=h1.nonce) - h1.update(data) - h2.update(data_ba) - data_ba[:1] = b'\xFF' - - self.assertEqual(h1.digest(), h2.digest()) - - def test_memoryview(self): - - data = b"\x00\x01\x02" - - def get_mv_ro(data): - return memoryview(data) - - def get_mv_rw(data): - return memoryview(bytearray(data)) - - for get_mv in (get_mv_ro, get_mv_rw): - - # Data and key can be a memoryview (during initialization) - key_mv = get_mv(self.key) - data_mv = get_mv(data) - - h1 = Poly1305.new(key=self.key, data=data, cipher=AES) - h2 = Poly1305.new(key=key_mv, data=data_mv, cipher=AES, - nonce=h1.nonce) - if not data_mv.readonly: - data_mv[:1] = b'\xFF' - key_mv[:1] = b'\xFF' - - self.assertEqual(h1.digest(), h2.digest()) - - # Data can be a memoryview (during operation) - data_mv = get_mv(data) - - h1 = Poly1305.new(key=self.key, cipher=AES) - h2 = Poly1305.new(key=self.key, cipher=AES, nonce=h1.nonce) - h1.update(data) - h2.update(data_mv) - if not data_mv.readonly: - data_mv[:1] = b'\xFF' - - self.assertEqual(h1.digest(), h2.digest()) - - -class Poly1305Test_ChaCha20(unittest.TestCase): - - key = b'\x11' * 32 - - def test_new_positive(self): - data = b'r' * 100 - - h1 = Poly1305.new(key=self.key, cipher=ChaCha20) - self.assertEqual(h1.digest_size, 16) - self.assertEqual(len(h1.nonce), 12) - - h2 = Poly1305.new(key=self.key, cipher=ChaCha20, nonce = b'8' * 8) - self.assertEqual(len(h2.nonce), 8) - self.assertEqual(h2.nonce, b'8' * 8) - - def test_new_negative(self): - - self.assertRaises(ValueError, Poly1305.new, key=self.key, nonce=b'1' * 7, cipher=ChaCha20) - - -# -# make_mac_tests() expect a new() function with signature new(key, data, -# **kwargs), and we need to adapt Poly1305's, as it only uses keywords -# -class Poly1305_New(object): - - @staticmethod - def new(key, *data, **kwds): - _kwds = dict(kwds) - if len(data) == 1: - _kwds['data'] = data[0] - _kwds['key'] = key - return Poly1305.new(**_kwds) - - -class Poly1305_Basic(object): - - @staticmethod - def new(key, *data, **kwds): - from Cryptodome.Hash.Poly1305 import Poly1305_MAC - - if len(data) == 1: - msg = data[0] - else: - msg = None - - return Poly1305_MAC(key[:16], key[16:], msg) - - -class Poly1305AES_MC(unittest.TestCase): - - def runTest(self): - tag = unhexlify(b"fb447350c4e868c52ac3275cf9d4327e") - - msg = b'' - for msg_len in range(5000 + 1): - key = tag + strxor_c(tag, 0xFF) - nonce = tag[::-1] - if msg_len > 0: - msg = msg + tobytes(tag[0]) - auth = Poly1305.new(key=key, nonce=nonce, cipher=AES, data=msg) - tag = auth.digest() - - # Compare against output of original DJB's poly1305aes-20050218 - self.assertEqual("CDFA436DDD629C7DC20E1128530BAED2", auth.hexdigest().upper()) - - -def get_tests(config={}): - tests = make_mac_tests(Poly1305_Basic, "Poly1305", test_data_basic) - tests += make_mac_tests(Poly1305_New, "Poly1305", test_data_aes) - tests += make_mac_tests(Poly1305_New, "Poly1305", test_data_chacha20) - tests += [ Poly1305AES_MC() ] - tests += list_test_cases(Poly1305Test_AES) - tests += list_test_cases(Poly1305Test_ChaCha20) - return tests - - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_RIPEMD160.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_RIPEMD160.py deleted file mode 100644 index c05a877..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_RIPEMD160.py +++ /dev/null @@ -1,71 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Hash/test_RIPEMD160.py: Self-test for the RIPEMD-160 hash function -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -#"""Self-test suite for Cryptodome.Hash.RIPEMD160""" - -from Cryptodome.Util.py3compat import * - -# This is a list of (expected_result, input[, description]) tuples. -test_data = [ - # Test vectors downloaded 2008-09-12 from - # http://homes.esat.kuleuven.be/~bosselae/ripemd160.html - ('9c1185a5c5e9fc54612808977ee8f548b2258d31', '', "'' (empty string)"), - ('0bdc9d2d256b3ee9daae347be6f4dc835a467ffe', 'a'), - ('8eb208f7e05d987a9b044a8e98c6b087f15a0bfc', 'abc'), - ('5d0689ef49d2fae572b881b123a85ffa21595f36', 'message digest'), - - ('f71c27109c692c1b56bbdceb5b9d2865b3708dbc', - 'abcdefghijklmnopqrstuvwxyz', - 'a-z'), - - ('12a053384a9c0c88e405a06c27dcf49ada62eb2b', - 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', - 'abcdbcd...pnopq'), - - ('b0e20b6e3116640286ed3a87a5713079b21f5189', - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', - 'A-Z, a-z, 0-9'), - - ('9b752e45573d4b39f4dbd3323cab82bf63326bfb', - '1234567890' * 8, - "'1234567890' * 8"), - - ('52783243c1697bdbe16d37f97f68f08325dc1528', - 'a' * 10**6, - '"a" * 10**6'), -] - -def get_tests(config={}): - from Cryptodome.Hash import RIPEMD160 - from .common import make_hash_tests - return make_hash_tests(RIPEMD160, "RIPEMD160", test_data, - digest_size=20, - oid="1.3.36.3.2.1") - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA1.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA1.py deleted file mode 100644 index a879e68..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA1.py +++ /dev/null @@ -1,84 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Hash/SHA1.py: Self-test for the SHA-1 hash function -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Hash.SHA""" - -from binascii import hexlify - -from Cryptodome.SelfTest.loader import load_test_vectors - -# Test vectors from various sources -# This is a list of (expected_result, input[, description]) tuples. -test_data_various = [ - # FIPS PUB 180-2, A.1 - "One-Block Message" - ('a9993e364706816aba3e25717850c26c9cd0d89d', 'abc'), - - # FIPS PUB 180-2, A.2 - "Multi-Block Message" - ('84983e441c3bd26ebaae4aa1f95129e5e54670f1', - 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'), - - # FIPS PUB 180-2, A.3 - "Long Message" -# ('34aa973cd4c4daa4f61eeb2bdbad27316534016f', -# 'a' * 10**6, -# '"a" * 10**6'), - - # RFC 3174: Section 7.3, "TEST4" (multiple of 512 bits) - ('dea356a2cddd90c7a7ecedc5ebb563934f460452', - '01234567' * 80, - '"01234567" * 80'), -] - -def get_tests(config={}): - from Cryptodome.Hash import SHA1 - from .common import make_hash_tests - - tests = [] - - test_vectors = load_test_vectors(("Hash", "SHA1"), - "SHA1ShortMsg.rsp", - "KAT SHA-1", - { "len" : lambda x: int(x) } ) or [] - - test_data = test_data_various[:] - for tv in test_vectors: - try: - if tv.startswith('['): - continue - except AttributeError: - pass - if tv.len == 0: - tv.msg = b"" - test_data.append((hexlify(tv.md), tv.msg, tv.desc)) - - tests = make_hash_tests(SHA1, "SHA1", test_data, - digest_size=20, - oid="1.3.14.3.2.26") - return tests - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA224.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA224.py deleted file mode 100644 index da32423..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA224.py +++ /dev/null @@ -1,63 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Hash/test_SHA224.py: Self-test for the SHA-224 hash function -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Hash.SHA224""" - -# Test vectors from various sources -# This is a list of (expected_result, input[, description]) tuples. -test_data = [ - - # RFC 3874: Section 3.1, "Test Vector #1 - ('23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7', 'abc'), - - # RFC 3874: Section 3.2, "Test Vector #2 - ('75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525', 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'), - - # RFC 3874: Section 3.3, "Test Vector #3 - ('20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67', 'a' * 10**6, "'a' * 10**6"), - - # Examples from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm - ('d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f', ''), - - ('49b08defa65e644cbf8a2dd9270bdededabc741997d1dadd42026d7b', - 'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern'), - - ('58911e7fccf2971a7d07f93162d8bd13568e71aa8fc86fc1fe9043d1', - 'Frank jagt im komplett verwahrlosten Taxi quer durch Bayern'), - -] - -def get_tests(config={}): - from Cryptodome.Hash import SHA224 - from .common import make_hash_tests - return make_hash_tests(SHA224, "SHA224", test_data, - digest_size=28, - oid='2.16.840.1.101.3.4.2.4') - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA256.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA256.py deleted file mode 100644 index 23d1145..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA256.py +++ /dev/null @@ -1,94 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Hash/test_SHA256.py: Self-test for the SHA-256 hash function -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Hash.SHA256""" - -import unittest -from Cryptodome.Util.py3compat import * - -class LargeSHA256Test(unittest.TestCase): - def runTest(self): - """SHA256: 512/520 MiB test""" - from Cryptodome.Hash import SHA256 - zeros = bchr(0x00) * (1024*1024) - - h = SHA256.new(zeros) - for i in range(511): - h.update(zeros) - - # This test vector is from PyCrypto's old testdata.py file. - self.assertEqual('9acca8e8c22201155389f65abbf6bc9723edc7384ead80503839f49dcc56d767', h.hexdigest()) # 512 MiB - - for i in range(8): - h.update(zeros) - - # This test vector is from PyCrypto's old testdata.py file. - self.assertEqual('abf51ad954b246009dfe5a50ecd582fd5b8f1b8b27f30393853c3ef721e7fa6e', h.hexdigest()) # 520 MiB - -def get_tests(config={}): - # Test vectors from FIPS PUB 180-2 - # This is a list of (expected_result, input[, description]) tuples. - test_data = [ - # FIPS PUB 180-2, B.1 - "One-Block Message" - ('ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad', - 'abc'), - - # FIPS PUB 180-2, B.2 - "Multi-Block Message" - ('248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1', - 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'), - - # FIPS PUB 180-2, B.3 - "Long Message" - ('cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0', - 'a' * 10**6, - '"a" * 10**6'), - - # Test for an old PyCryptodome bug. - ('f7fd017a3c721ce7ff03f3552c0813adcc48b7f33f07e5e2ba71e23ea393d103', - 'This message is precisely 55 bytes long, to test a bug.', - 'Length = 55 (mod 64)'), - - # Example from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm - ('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', ''), - - ('d32b568cd1b96d459e7291ebf4b25d007f275c9f13149beeb782fac0716613f8', - 'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern'), - ] - - from Cryptodome.Hash import SHA256 - from .common import make_hash_tests - tests = make_hash_tests(SHA256, "SHA256", test_data, - digest_size=32, - oid="2.16.840.1.101.3.4.2.1") - - if config.get('slow_tests'): - tests += [LargeSHA256Test()] - - return tests - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA384.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA384.py deleted file mode 100644 index 5233d13..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA384.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Hash/test_SHA.py: Self-test for the SHA-384 hash function -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Hash.SHA384""" - -# Test vectors from various sources -# This is a list of (expected_result, input[, description]) tuples. -test_data = [ - - # RFC 4634: Section Page 8.4, "Test 1" - ('cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7', 'abc'), - - # RFC 4634: Section Page 8.4, "Test 2.2" - ('09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039', 'abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu'), - - # RFC 4634: Section Page 8.4, "Test 3" - ('9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985', 'a' * 10**6, "'a' * 10**6"), - - # Taken from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm - ('38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b', ''), - - # Example from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm - ('71e8383a4cea32d6fd6877495db2ee353542f46fa44bc23100bca48f3366b84e809f0708e81041f427c6d5219a286677', - 'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern'), - -] - -def get_tests(config={}): - from Cryptodome.Hash import SHA384 - from .common import make_hash_tests - return make_hash_tests(SHA384, "SHA384", test_data, - digest_size=48, - oid='2.16.840.1.101.3.4.2.2') - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA3_224.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA3_224.py deleted file mode 100644 index 3141880..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA3_224.py +++ /dev/null @@ -1,79 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Hash/test_SHA3_224.py: Self-test for the SHA-3/224 hash function -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Hash.SHA3_224""" - -import unittest -from binascii import hexlify - -from Cryptodome.SelfTest.loader import load_test_vectors -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.Hash import SHA3_224 as SHA3 -from Cryptodome.Util.py3compat import b - - -class APITest(unittest.TestCase): - - def test_update_after_digest(self): - msg=b("rrrrttt") - - # Normally, update() cannot be done after digest() - h = SHA3.new(data=msg[:4]) - dig1 = h.digest() - self.assertRaises(TypeError, h.update, msg[4:]) - dig2 = SHA3.new(data=msg).digest() - - # With the proper flag, it is allowed - h = SHA3.new(data=msg[:4], update_after_digest=True) - self.assertEqual(h.digest(), dig1) - # ... and the subsequent digest applies to the entire message - # up to that point - h.update(msg[4:]) - self.assertEqual(h.digest(), dig2) - - -def get_tests(config={}): - from .common import make_hash_tests - - tests = [] - - test_vectors = load_test_vectors(("Hash", "SHA3"), - "ShortMsgKAT_SHA3-224.txt", - "KAT SHA-3 224", - { "len" : lambda x: int(x) } ) or [] - - test_data = [] - for tv in test_vectors: - if tv.len == 0: - tv.msg = b("") - test_data.append((hexlify(tv.md), tv.msg, tv.desc)) - - tests += make_hash_tests(SHA3, "SHA3_224", test_data, - digest_size=SHA3.digest_size, - oid="2.16.840.1.101.3.4.2.7") - tests += list_test_cases(APITest) - return tests - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA3_256.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA3_256.py deleted file mode 100644 index 9dee551..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA3_256.py +++ /dev/null @@ -1,80 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Hash/test_SHA3_256.py: Self-test for the SHA-3/256 hash function -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Hash.SHA3_256""" - -import unittest -from binascii import hexlify - -from Cryptodome.SelfTest.loader import load_test_vectors -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.Hash import SHA3_256 as SHA3 -from Cryptodome.Util.py3compat import b - - -class APITest(unittest.TestCase): - - def test_update_after_digest(self): - msg=b("rrrrttt") - - # Normally, update() cannot be done after digest() - h = SHA3.new(data=msg[:4]) - dig1 = h.digest() - self.assertRaises(TypeError, h.update, msg[4:]) - dig2 = SHA3.new(data=msg).digest() - - # With the proper flag, it is allowed - h = SHA3.new(data=msg[:4], update_after_digest=True) - self.assertEqual(h.digest(), dig1) - # ... and the subsequent digest applies to the entire message - # up to that point - h.update(msg[4:]) - self.assertEqual(h.digest(), dig2) - - -def get_tests(config={}): - from .common import make_hash_tests - - tests = [] - - test_vectors = load_test_vectors(("Hash", "SHA3"), - "ShortMsgKAT_SHA3-256.txt", - "KAT SHA-3 256", - { "len" : lambda x: int(x) } ) or [] - - test_data = [] - for tv in test_vectors: - if tv.len == 0: - tv.msg = b("") - test_data.append((hexlify(tv.md), tv.msg, tv.desc)) - - - tests += make_hash_tests(SHA3, "SHA3_256", test_data, - digest_size=SHA3.digest_size, - oid="2.16.840.1.101.3.4.2.8") - tests += list_test_cases(APITest) - return tests - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA3_384.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA3_384.py deleted file mode 100644 index c5030b5..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA3_384.py +++ /dev/null @@ -1,79 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Hash/test_SHA3_384.py: Self-test for the SHA-3/384 hash function -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Hash.SHA3_384""" - -import unittest -from binascii import hexlify - -from Cryptodome.SelfTest.loader import load_test_vectors -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.Hash import SHA3_384 as SHA3 -from Cryptodome.Util.py3compat import b - - -class APITest(unittest.TestCase): - - def test_update_after_digest(self): - msg=b("rrrrttt") - - # Normally, update() cannot be done after digest() - h = SHA3.new(data=msg[:4]) - dig1 = h.digest() - self.assertRaises(TypeError, h.update, msg[4:]) - dig2 = SHA3.new(data=msg).digest() - - # With the proper flag, it is allowed - h = SHA3.new(data=msg[:4], update_after_digest=True) - self.assertEqual(h.digest(), dig1) - # ... and the subsequent digest applies to the entire message - # up to that point - h.update(msg[4:]) - self.assertEqual(h.digest(), dig2) - - -def get_tests(config={}): - from .common import make_hash_tests - - tests = [] - - test_vectors = load_test_vectors(("Hash", "SHA3"), - "ShortMsgKAT_SHA3-384.txt", - "KAT SHA-3 384", - { "len" : lambda x: int(x) } ) or [] - - test_data = [] - for tv in test_vectors: - if tv.len == 0: - tv.msg = b("") - test_data.append((hexlify(tv.md), tv.msg, tv.desc)) - - tests += make_hash_tests(SHA3, "SHA3_384", test_data, - digest_size=SHA3.digest_size, - oid="2.16.840.1.101.3.4.2.9") - tests += list_test_cases(APITest) - return tests - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA3_512.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA3_512.py deleted file mode 100644 index b7a57f8..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA3_512.py +++ /dev/null @@ -1,79 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Hash/test_SHA3_512.py: Self-test for the SHA-3/512 hash function -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Hash.SHA3_512""" - -import unittest -from binascii import hexlify - -from Cryptodome.SelfTest.loader import load_test_vectors -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.Hash import SHA3_512 as SHA3 -from Cryptodome.Util.py3compat import b - - -class APITest(unittest.TestCase): - - def test_update_after_digest(self): - msg=b("rrrrttt") - - # Normally, update() cannot be done after digest() - h = SHA3.new(data=msg[:4]) - dig1 = h.digest() - self.assertRaises(TypeError, h.update, msg[4:]) - dig2 = SHA3.new(data=msg).digest() - - # With the proper flag, it is allowed - h = SHA3.new(data=msg[:4], update_after_digest=True) - self.assertEqual(h.digest(), dig1) - # ... and the subsequent digest applies to the entire message - # up to that point - h.update(msg[4:]) - self.assertEqual(h.digest(), dig2) - - -def get_tests(config={}): - from .common import make_hash_tests - - tests = [] - - test_vectors = load_test_vectors(("Hash", "SHA3"), - "ShortMsgKAT_SHA3-512.txt", - "KAT SHA-3 512", - { "len" : lambda x: int(x) } ) or [] - - test_data = [] - for tv in test_vectors: - if tv.len == 0: - tv.msg = b("") - test_data.append((hexlify(tv.md), tv.msg, tv.desc)) - - tests += make_hash_tests(SHA3, "SHA3_512", test_data, - digest_size=SHA3.digest_size, - oid="2.16.840.1.101.3.4.2.10") - tests += list_test_cases(APITest) - return tests - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA512.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA512.py deleted file mode 100644 index e6c74b3..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHA512.py +++ /dev/null @@ -1,140 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Hash/test_SHA512.py: Self-test for the SHA-512 hash function -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Hash.SHA512""" - -from binascii import hexlify - -from Cryptodome.Hash import SHA512 -from .common import make_hash_tests -from Cryptodome.SelfTest.loader import load_test_vectors - -# Test vectors from various sources -# This is a list of (expected_result, input[, description]) tuples. -test_data_512_other = [ - - # RFC 4634: Section Page 8.4, "Test 1" - ('ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f', 'abc'), - - # RFC 4634: Section Page 8.4, "Test 2.1" - ('8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909', 'abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu'), - - # RFC 4634: Section Page 8.4, "Test 3" - ('e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b', 'a' * 10**6, "'a' * 10**6"), - - # Taken from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm - ('cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e', ''), - - ('af9ed2de700433b803240a552b41b5a472a6ef3fe1431a722b2063c75e9f07451f67a28e37d09cde769424c96aea6f8971389db9e1993d6c565c3c71b855723c', 'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern'), -] - - -def get_tests_SHA512(): - - test_vectors = load_test_vectors(("Hash", "SHA2"), - "SHA512ShortMsg.rsp", - "KAT SHA-512", - {"len": lambda x: int(x)}) or [] - - test_data = test_data_512_other[:] - for tv in test_vectors: - try: - if tv.startswith('['): - continue - except AttributeError: - pass - if tv.len == 0: - tv.msg = b"" - test_data.append((hexlify(tv.md), tv.msg, tv.desc)) - - tests = make_hash_tests(SHA512, "SHA512", test_data, - digest_size=64, - oid="2.16.840.1.101.3.4.2.3") - return tests - - -def get_tests_SHA512_224(): - - test_vectors = load_test_vectors(("Hash", "SHA2"), - "SHA512_224ShortMsg.rsp", - "KAT SHA-512/224", - {"len": lambda x: int(x)}) or [] - - test_data = [] - for tv in test_vectors: - try: - if tv.startswith('['): - continue - except AttributeError: - pass - if tv.len == 0: - tv.msg = b"" - test_data.append((hexlify(tv.md), tv.msg, tv.desc)) - - tests = make_hash_tests(SHA512, "SHA512/224", test_data, - digest_size=28, - oid="2.16.840.1.101.3.4.2.5", - extra_params={ "truncate" : "224" }) - return tests - - -def get_tests_SHA512_256(): - - test_vectors = load_test_vectors(("Hash", "SHA2"), - "SHA512_256ShortMsg.rsp", - "KAT SHA-512/256", - {"len": lambda x: int(x)}) or [] - - test_data = [] - for tv in test_vectors: - try: - if tv.startswith('['): - continue - except AttributeError: - pass - if tv.len == 0: - tv.msg = b"" - test_data.append((hexlify(tv.md), tv.msg, tv.desc)) - - tests = make_hash_tests(SHA512, "SHA512/256", test_data, - digest_size=32, - oid="2.16.840.1.101.3.4.2.6", - extra_params={ "truncate" : "256" }) - return tests - - -def get_tests(config={}): - - tests = [] - tests += get_tests_SHA512() - tests += get_tests_SHA512_224() - tests += get_tests_SHA512_256() - return tests - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHAKE.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHAKE.py deleted file mode 100644 index 2283308..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_SHAKE.py +++ /dev/null @@ -1,143 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2015, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -"""Self-test suite for Cryptodome.Hash.SHAKE128 and SHAKE256""" - -import unittest -from binascii import hexlify, unhexlify - -from Cryptodome.SelfTest.loader import load_test_vectors -from Cryptodome.SelfTest.st_common import list_test_cases - -from Cryptodome.Hash import SHAKE128, SHAKE256 -from Cryptodome.Util.py3compat import b, bchr, bord, tobytes - -class SHAKETest(unittest.TestCase): - - def test_new_positive(self): - - xof1 = self.shake.new() - xof2 = self.shake.new(data=b("90")) - xof3 = self.shake.new().update(b("90")) - - self.assertNotEqual(xof1.read(10), xof2.read(10)) - xof3.read(10) - self.assertEqual(xof2.read(10), xof3.read(10)) - - def test_update(self): - pieces = [bchr(10) * 200, bchr(20) * 300] - h = self.shake.new() - h.update(pieces[0]).update(pieces[1]) - digest = h.read(10) - h = self.shake.new() - h.update(pieces[0] + pieces[1]) - self.assertEqual(h.read(10), digest) - - def test_update_negative(self): - h = self.shake.new() - self.assertRaises(TypeError, h.update, u"string") - - def test_digest(self): - h = self.shake.new() - digest = h.read(90) - - # read returns a byte string of the right length - self.assertTrue(isinstance(digest, type(b("digest")))) - self.assertEqual(len(digest), 90) - - def test_update_after_read(self): - mac = self.shake.new() - mac.update(b("rrrr")) - mac.read(90) - self.assertRaises(TypeError, mac.update, b("ttt")) - - -class SHAKE128Test(SHAKETest): - shake = SHAKE128 - - -class SHAKE256Test(SHAKETest): - shake = SHAKE256 - - -class SHAKEVectors(unittest.TestCase): - pass - - -test_vectors_128 = load_test_vectors(("Hash", "SHA3"), - "ShortMsgKAT_SHAKE128.txt", - "Short Messages KAT SHAKE128", - { "len" : lambda x: int(x) } ) or [] - -for idx, tv in enumerate(test_vectors_128): - if tv.len == 0: - data = b("") - else: - data = tobytes(tv.msg) - - def new_test(self, data=data, result=tv.md): - hobj = SHAKE128.new(data=data) - digest = hobj.read(len(result)) - self.assertEqual(digest, result) - - setattr(SHAKEVectors, "test_128_%d" % idx, new_test) - - -test_vectors_256 = load_test_vectors(("Hash", "SHA3"), - "ShortMsgKAT_SHAKE256.txt", - "Short Messages KAT SHAKE256", - { "len" : lambda x: int(x) } ) or [] - -for idx, tv in enumerate(test_vectors_256): - if tv.len == 0: - data = b("") - else: - data = tobytes(tv.msg) - - def new_test(self, data=data, result=tv.md): - hobj = SHAKE256.new(data=data) - digest = hobj.read(len(result)) - self.assertEqual(digest, result) - - setattr(SHAKEVectors, "test_256_%d" % idx, new_test) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(SHAKE128Test) - tests += list_test_cases(SHAKE256Test) - tests += list_test_cases(SHAKEVectors) - return tests - - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_TupleHash.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_TupleHash.py deleted file mode 100644 index 2f93d7b..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_TupleHash.py +++ /dev/null @@ -1,302 +0,0 @@ -import unittest -from binascii import unhexlify, hexlify - -from Cryptodome.Util.py3compat import tobytes -from Cryptodome.SelfTest.st_common import list_test_cases - -from Cryptodome.Hash import TupleHash128, TupleHash256 - - -class TupleHashTest(unittest.TestCase): - - def new(self, *args, **kwargs): - return self.TupleHash.new(*args, **kwargs) - - def test_new_positive(self): - - h = self.new() - for new_func in self.TupleHash.new, h.new: - - for dbits in range(64, 1024 + 1, 8): - hobj = new_func(digest_bits=dbits) - self.assertEqual(hobj.digest_size * 8, dbits) - - for dbytes in range(8, 128 + 1): - hobj = new_func(digest_bytes=dbytes) - self.assertEqual(hobj.digest_size, dbytes) - - hobj = h.new() - self.assertEqual(hobj.digest_size, self.default_bytes) - - def test_new_negative(self): - - h = self.new() - for new_func in self.TupleHash.new, h.new: - self.assertRaises(TypeError, new_func, - digest_bytes=self.minimum_bytes, - digest_bits=self.minimum_bits) - self.assertRaises(ValueError, new_func, digest_bytes=0) - self.assertRaises(ValueError, new_func, - digest_bits=self.minimum_bits + 7) - self.assertRaises(ValueError, new_func, - digest_bits=self.minimum_bits - 8) - self.assertRaises(ValueError, new_func, - digest_bits=self.minimum_bytes - 1) - - def test_default_digest_size(self): - digest = self.new().digest() - self.assertEqual(len(digest), self.default_bytes) - - def test_update(self): - h = self.new() - h.update(b'') - h.digest() - - h = self.new() - h.update(b'') - h.update(b'STRING1') - h.update(b'STRING2') - mac1 = h.digest() - - h = self.new() - h.update(b'STRING1') - h.update(b'STRING2') - mac2 = h.digest() - self.assertNotEqual(mac1, mac2) - - h = self.new() - h.update(b'STRING1', b'STRING2') - self.assertEqual(mac2, h.digest()) - - h = self.new() - t = b'STRING1', b'STRING2' - h.update(*t) - self.assertEqual(mac2, h.digest()) - - def test_update_negative(self): - h = self.new() - self.assertRaises(TypeError, h.update, u"string") - self.assertRaises(TypeError, h.update, None) - self.assertRaises(TypeError, h.update, (b'STRING1', b'STRING2')) - - def test_digest(self): - h = self.new() - digest = h.digest() - - # hexdigest does not change the state - self.assertEqual(h.digest(), digest) - # digest returns a byte string - self.assertTrue(isinstance(digest, type(b"digest"))) - - def test_update_after_digest(self): - msg = b"rrrrttt" - - # Normally, update() cannot be done after digest() - h = self.new() - h.update(msg) - dig1 = h.digest() - self.assertRaises(TypeError, h.update, dig1) - - def test_hex_digest(self): - mac = self.new() - digest = mac.digest() - hexdigest = mac.hexdigest() - - # hexdigest is equivalent to digest - self.assertEqual(hexlify(digest), tobytes(hexdigest)) - # hexdigest does not change the state - self.assertEqual(mac.hexdigest(), hexdigest) - # hexdigest returns a string - self.assertTrue(isinstance(hexdigest, type("digest"))) - - def test_bytearray(self): - - data = b"\x00\x01\x02" - - # Data can be a bytearray (during operation) - data_ba = bytearray(data) - - h1 = self.new() - h2 = self.new() - h1.update(data) - h2.update(data_ba) - data_ba[:1] = b'\xFF' - - self.assertEqual(h1.digest(), h2.digest()) - - def test_memoryview(self): - - data = b"\x00\x01\x02" - - def get_mv_ro(data): - return memoryview(data) - - def get_mv_rw(data): - return memoryview(bytearray(data)) - - for get_mv in (get_mv_ro, get_mv_rw): - - # Data can be a memoryview (during operation) - data_mv = get_mv(data) - - h1 = self.new() - h2 = self.new() - h1.update(data) - h2.update(data_mv) - if not data_mv.readonly: - data_mv[:1] = b'\xFF' - - self.assertEqual(h1.digest(), h2.digest()) - - -class TupleHash128Test(TupleHashTest): - - TupleHash = TupleHash128 - - minimum_bytes = 8 - default_bytes = 64 - - minimum_bits = 64 - default_bits = 512 - - -class TupleHash256Test(TupleHashTest): - - TupleHash = TupleHash256 - - minimum_bytes = 8 - default_bytes = 64 - - minimum_bits = 64 - default_bits = 512 - - -class NISTExampleTestVectors(unittest.TestCase): - - # http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/TupleHash_samples.pdf - test_data = [ - ( - ( - "00 01 02", - "10 11 12 13 14 15", - ), - "", - "C5 D8 78 6C 1A FB 9B 82 11 1A B3 4B 65 B2 C0 04" - "8F A6 4E 6D 48 E2 63 26 4C E1 70 7D 3F FC 8E D1", - "KMAC128 Sample #1 NIST", - TupleHash128 - ), - ( - ( - "00 01 02", - "10 11 12 13 14 15", - ), - "My Tuple App", - "75 CD B2 0F F4 DB 11 54 E8 41 D7 58 E2 41 60 C5" - "4B AE 86 EB 8C 13 E7 F5 F4 0E B3 55 88 E9 6D FB", - "KMAC128 Sample #2 NIST", - TupleHash128 - ), - ( - ( - "00 01 02", - "10 11 12 13 14 15", - "20 21 22 23 24 25 26 27 28", - ), - "My Tuple App", - "E6 0F 20 2C 89 A2 63 1E DA 8D 4C 58 8C A5 FD 07" - "F3 9E 51 51 99 8D EC CF 97 3A DB 38 04 BB 6E 84", - "KMAC128 Sample #3 NIST", - TupleHash128 - ), - ( - ( - "00 01 02", - "10 11 12 13 14 15", - ), - "", - "CF B7 05 8C AC A5 E6 68 F8 1A 12 A2 0A 21 95 CE" - "97 A9 25 F1 DB A3 E7 44 9A 56 F8 22 01 EC 60 73" - "11 AC 26 96 B1 AB 5E A2 35 2D F1 42 3B DE 7B D4" - "BB 78 C9 AE D1 A8 53 C7 86 72 F9 EB 23 BB E1 94", - "KMAC256 Sample #4 NIST", - TupleHash256 - ), - ( - ( - "00 01 02", - "10 11 12 13 14 15", - ), - "My Tuple App", - "14 7C 21 91 D5 ED 7E FD 98 DB D9 6D 7A B5 A1 16" - "92 57 6F 5F E2 A5 06 5F 3E 33 DE 6B BA 9F 3A A1" - "C4 E9 A0 68 A2 89 C6 1C 95 AA B3 0A EE 1E 41 0B" - "0B 60 7D E3 62 0E 24 A4 E3 BF 98 52 A1 D4 36 7E", - "KMAC256 Sample #5 NIST", - TupleHash256 - ), - ( - ( - "00 01 02", - "10 11 12 13 14 15", - "20 21 22 23 24 25 26 27 28", - ), - "My Tuple App", - "45 00 0B E6 3F 9B 6B FD 89 F5 47 17 67 0F 69 A9" - "BC 76 35 91 A4 F0 5C 50 D6 88 91 A7 44 BC C6 E7" - "D6 D5 B5 E8 2C 01 8D A9 99 ED 35 B0 BB 49 C9 67" - "8E 52 6A BD 8E 85 C1 3E D2 54 02 1D B9 E7 90 CE", - "KMAC256 Sample #6 NIST", - TupleHash256 - ), - - - - ] - - def setUp(self): - td = [] - for tv_in in self.test_data: - tv_out = [None] * len(tv_in) - - tv_out[0] = [] - for string in tv_in[0]: - tv_out[0].append(unhexlify(string.replace(" ", ""))) - - tv_out[1] = tobytes(tv_in[1]) # Custom - tv_out[2] = unhexlify(tv_in[2].replace(" ", "")) - tv_out[3] = tv_in[3] - tv_out[4] = tv_in[4] - td.append(tv_out) - self.test_data = td - - def runTest(self): - - for data, custom, digest, text, module in self.test_data: - hd1 = module.new(custom=custom, digest_bytes=len(digest)) - hd2 = module.new(custom=custom, digest_bytes=len(digest)) - - # Call update() for each element - for string in data: - hd1.update(string) - - # One single update for all elements - hd2.update(*data) - - self.assertEqual(hd1.digest(), digest, msg=text) - self.assertEqual(hd2.digest(), digest, msg=text) - -def get_tests(config={}): - tests = [] - - tests += list_test_cases(TupleHash128Test) - tests += list_test_cases(TupleHash256Test) - tests.append(NISTExampleTestVectors()) - - return tests - - -if __name__ == '__main__': - def suite(): - return unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_TurboSHAKE.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_TurboSHAKE.py deleted file mode 100644 index 7c13d1e..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_TurboSHAKE.py +++ /dev/null @@ -1,468 +0,0 @@ -"""Self-test suite for Cryptodome.Hash.TurboSHAKE128 and TurboSHAKE256""" - -import unittest -from binascii import unhexlify - -from Cryptodome.SelfTest.st_common import list_test_cases - -from Cryptodome.Hash import TurboSHAKE128, TurboSHAKE256 -from Cryptodome.Util.py3compat import bchr - - -class TurboSHAKETest(unittest.TestCase): - - def test_new_positive(self): - - xof1 = self.TurboSHAKE.new() - xof1.update(b'90') - - xof2 = self.TurboSHAKE.new(domain=0x1F) - xof2.update(b'90') - - xof3 = self.TurboSHAKE.new(data=b'90') - - out1 = xof1.read(128) - out2 = xof2.read(128) - out3 = xof3.read(128) - - self.assertEqual(out1, out2) - self.assertEqual(out1, out3) - - def test_new_domain(self): - xof1 = self.TurboSHAKE.new(domain=0x1D) - xof2 = self.TurboSHAKE.new(domain=0x20) - self.assertNotEqual(xof1.read(128), xof2.read(128)) - - def test_update(self): - pieces = [bchr(10) * 200, bchr(20) * 300] - - xof1 = self.TurboSHAKE.new() - xof1.update(pieces[0]).update(pieces[1]) - digest1 = xof1.read(10) - - xof2 = self.TurboSHAKE.new() - xof2.update(pieces[0] + pieces[1]) - digest2 = xof2.read(10) - - self.assertEqual(digest1, digest2) - - def test_update_negative(self): - xof1 = self.TurboSHAKE.new() - self.assertRaises(TypeError, xof1.update, u"string") - - def test_read(self): - xof1 = self.TurboSHAKE.new() - digest = xof1.read(90) - - # read returns a byte string of the right length - self.assertTrue(isinstance(digest, bytes)) - self.assertEqual(len(digest), 90) - - def test_update_after_read(self): - xof1 = self.TurboSHAKE.new() - xof1.update(b"rrrr") - xof1.read(90) - self.assertRaises(TypeError, xof1.update, b"ttt") - - def test_new(self): - xof1 = self.TurboSHAKE.new(domain=0x07) - xof1.update(b'90') - digest1 = xof1.read(100) - - xof2 = xof1.new() - xof2.update(b'90') - digest2 = xof2.read(100) - - self.assertEqual(digest1, digest2) - - self.assertRaises(TypeError, xof1.new, domain=0x07) - - -class TurboSHAKE128Test(TurboSHAKETest): - TurboSHAKE = TurboSHAKE128 - - -class TurboSHAKE256Test(TurboSHAKETest): - TurboSHAKE = TurboSHAKE256 - - -def txt2bin(txt): - clean = txt.replace(" ", "").replace("\n", "").replace("\r", "") - return unhexlify(clean) - - -def ptn(n): - res = bytearray(n) - pattern = b"".join([bchr(x) for x in range(0, 0xFB)]) - for base in range(0, n - 0xFB, 0xFB): - res[base:base + 0xFB] = pattern - remain = n % 0xFB - if remain: - base = (n // 0xFB) * 0xFB - res[base:] = pattern[:remain] - assert len(res) == n - return res - - -def chunked(source, size): - for i in range(0, len(source), size): - yield source[i:i+size] - - -class TurboSHAKE128TV(unittest.TestCase): - - def test_zero_1(self): - tv = """1E 41 5F 1C 59 83 AF F2 16 92 17 27 7D 17 BB 53 - 8C D9 45 A3 97 DD EC 54 1F 1C E4 1A F2 C1 B7 4C""" - - btv = txt2bin(tv) - res = TurboSHAKE128.new().read(32) - self.assertEqual(res, btv) - - def test_zero_2(self): - tv = """1E 41 5F 1C 59 83 AF F2 16 92 17 27 7D 17 BB 53 - 8C D9 45 A3 97 DD EC 54 1F 1C E4 1A F2 C1 B7 4C - 3E 8C CA E2 A4 DA E5 6C 84 A0 4C 23 85 C0 3C 15 - E8 19 3B DF 58 73 73 63 32 16 91 C0 54 62 C8 DF""" - - btv = txt2bin(tv) - res = TurboSHAKE128.new().read(64) - self.assertEqual(res, btv) - - def test_zero_3(self): - tv = """A3 B9 B0 38 59 00 CE 76 1F 22 AE D5 48 E7 54 DA - 10 A5 24 2D 62 E8 C6 58 E3 F3 A9 23 A7 55 56 07""" - - btv = txt2bin(tv) - res = TurboSHAKE128.new().read(10032)[-32:] - self.assertEqual(res, btv) - - def test_ptn_1(self): - tv = """55 CE DD 6F 60 AF 7B B2 9A 40 42 AE 83 2E F3 F5 - 8D B7 29 9F 89 3E BB 92 47 24 7D 85 69 58 DA A9""" - - btv = txt2bin(tv) - res = TurboSHAKE128.new(data=ptn(1)).read(32) - self.assertEqual(res, btv) - - def test_ptn_17(self): - tv = """9C 97 D0 36 A3 BA C8 19 DB 70 ED E0 CA 55 4E C6 - E4 C2 A1 A4 FF BF D9 EC 26 9C A6 A1 11 16 12 33""" - - btv = txt2bin(tv) - res = TurboSHAKE128.new(data=ptn(17)).read(32) - self.assertEqual(res, btv) - - def test_ptn_17_2(self): - tv = """96 C7 7C 27 9E 01 26 F7 FC 07 C9 B0 7F 5C DA E1 - E0 BE 60 BD BE 10 62 00 40 E7 5D 72 23 A6 24 D2""" - - btv = txt2bin(tv) - res = TurboSHAKE128.new(data=ptn(17**2)).read(32) - self.assertEqual(res, btv) - - def test_ptn_17_3(self): - tv = """D4 97 6E B5 6B CF 11 85 20 58 2B 70 9F 73 E1 D6 - 85 3E 00 1F DA F8 0E 1B 13 E0 D0 59 9D 5F B3 72""" - - btv = txt2bin(tv) - res = TurboSHAKE128.new(data=ptn(17**3)).read(32) - self.assertEqual(res, btv) - - def test_ptn_17_4(self): - tv = """DA 67 C7 03 9E 98 BF 53 0C F7 A3 78 30 C6 66 4E - 14 CB AB 7F 54 0F 58 40 3B 1B 82 95 13 18 EE 5C""" - - btv = txt2bin(tv) - data = ptn(17**4) - - # All at once - res = TurboSHAKE128.new(data=data).read(32) - self.assertEqual(res, btv) - - # Byte by byte - xof = TurboSHAKE128.new() - for x in data: - xof.update(bchr(x)) - res = xof.read(32) - self.assertEqual(res, btv) - - # Chunks of various prime sizes - for chunk_size in (13, 17, 19, 23, 31): - xof = TurboSHAKE128.new() - for x in chunked(data, chunk_size): - xof.update(x) - res = xof.read(32) - self.assertEqual(res, btv) - - def test_ptn_17_5(self): - tv = """B9 7A 90 6F BF 83 EF 7C 81 25 17 AB F3 B2 D0 AE - A0 C4 F6 03 18 CE 11 CF 10 39 25 12 7F 59 EE CD""" - - btv = txt2bin(tv) - data = ptn(17**5) - - # All at once - res = TurboSHAKE128.new(data=data).read(32) - self.assertEqual(res, btv) - - # Chunks - xof = TurboSHAKE128.new() - for chunk in chunked(data, 8192): - xof.update(chunk) - res = xof.read(32) - self.assertEqual(res, btv) - - def test_ptn_17_6(self): - tv = """35 CD 49 4A DE DE D2 F2 52 39 AF 09 A7 B8 EF 0C - 4D 1C A4 FE 2D 1A C3 70 FA 63 21 6F E7 B4 C2 B1""" - - btv = txt2bin(tv) - data = ptn(17**6) - - res = TurboSHAKE128.new(data=data).read(32) - self.assertEqual(res, btv) - - def test_ffffff_d01(self): - tv = """BF 32 3F 94 04 94 E8 8E E1 C5 40 FE 66 0B E8 A0 - C9 3F 43 D1 5E C0 06 99 84 62 FA 99 4E ED 5D AB""" - - btv = txt2bin(tv) - res = TurboSHAKE128.new(data=b"\xff\xff\xff", domain=0x01).read(32) - self.assertEqual(res, btv) - - def test_ff_d06(self): - tv = """8E C9 C6 64 65 ED 0D 4A 6C 35 D1 35 06 71 8D 68 - 7A 25 CB 05 C7 4C CA 1E 42 50 1A BD 83 87 4A 67""" - - btv = txt2bin(tv) - res = TurboSHAKE128.new(data=b'\xFF', domain=0x06).read(32) - self.assertEqual(res, btv) - - def test_ffffff_d07(self): - tv = """B6 58 57 60 01 CA D9 B1 E5 F3 99 A9 F7 77 23 BB - A0 54 58 04 2D 68 20 6F 72 52 68 2D BA 36 63 ED""" - - btv = txt2bin(tv) - res = TurboSHAKE128.new(data=b'\xFF' * 3, domain=0x07).read(32) - self.assertEqual(res, btv) - - def test_ffffffffffff_d0b(self): - tv = """8D EE AA 1A EC 47 CC EE 56 9F 65 9C 21 DF A8 E1 - 12 DB 3C EE 37 B1 81 78 B2 AC D8 05 B7 99 CC 37""" - - btv = txt2bin(tv) - res = TurboSHAKE128.new(data=b'\xFF' * 7, domain=0x0B).read(32) - self.assertEqual(res, btv) - - def test_ff_d30(self): - tv = """55 31 22 E2 13 5E 36 3C 32 92 BE D2 C6 42 1F A2 - 32 BA B0 3D AA 07 C7 D6 63 66 03 28 65 06 32 5B""" - - btv = txt2bin(tv) - res = TurboSHAKE128.new(data=b'\xFF', domain=0x30).read(32) - self.assertEqual(res, btv) - - def test_ffffff_d7f(self): - tv = """16 27 4C C6 56 D4 4C EF D4 22 39 5D 0F 90 53 BD - A6 D2 8E 12 2A BA 15 C7 65 E5 AD 0E 6E AF 26 F9""" - - btv = txt2bin(tv) - res = TurboSHAKE128.new(data=b'\xFF' * 3, domain=0x7F).read(32) - self.assertEqual(res, btv) - - -class TurboSHAKE256TV(unittest.TestCase): - - def test_zero_1(self): - tv = """36 7A 32 9D AF EA 87 1C 78 02 EC 67 F9 05 AE 13 - C5 76 95 DC 2C 66 63 C6 10 35 F5 9A 18 F8 E7 DB - 11 ED C0 E1 2E 91 EA 60 EB 6B 32 DF 06 DD 7F 00 - 2F BA FA BB 6E 13 EC 1C C2 0D 99 55 47 60 0D B0""" - - btv = txt2bin(tv) - res = TurboSHAKE256.new().read(64) - self.assertEqual(res, btv) - - def test_zero_2(self): - tv = """AB EF A1 16 30 C6 61 26 92 49 74 26 85 EC 08 2F - 20 72 65 DC CF 2F 43 53 4E 9C 61 BA 0C 9D 1D 75""" - - btv = txt2bin(tv) - res = TurboSHAKE256.new().read(10032)[-32:] - self.assertEqual(res, btv) - - def test_ptn_1(self): - tv = """3E 17 12 F9 28 F8 EA F1 05 46 32 B2 AA 0A 24 6E - D8 B0 C3 78 72 8F 60 BC 97 04 10 15 5C 28 82 0E - 90 CC 90 D8 A3 00 6A A2 37 2C 5C 5E A1 76 B0 68 - 2B F2 2B AE 74 67 AC 94 F7 4D 43 D3 9B 04 82 E2""" - - btv = txt2bin(tv) - res = TurboSHAKE256.new(data=ptn(1)).read(64) - self.assertEqual(res, btv) - - def test_ptn_17(self): - tv = """B3 BA B0 30 0E 6A 19 1F BE 61 37 93 98 35 92 35 - 78 79 4E A5 48 43 F5 01 10 90 FA 2F 37 80 A9 E5 - CB 22 C5 9D 78 B4 0A 0F BF F9 E6 72 C0 FB E0 97 - 0B D2 C8 45 09 1C 60 44 D6 87 05 4D A5 D8 E9 C7""" - - btv = txt2bin(tv) - res = TurboSHAKE256.new(data=ptn(17)).read(64) - self.assertEqual(res, btv) - - def test_ptn_17_2(self): - tv = """66 B8 10 DB 8E 90 78 04 24 C0 84 73 72 FD C9 57 - 10 88 2F DE 31 C6 DF 75 BE B9 D4 CD 93 05 CF CA - E3 5E 7B 83 E8 B7 E6 EB 4B 78 60 58 80 11 63 16 - FE 2C 07 8A 09 B9 4A D7 B8 21 3C 0A 73 8B 65 C0""" - - btv = txt2bin(tv) - res = TurboSHAKE256.new(data=ptn(17**2)).read(64) - self.assertEqual(res, btv) - - def test_ptn_17_3(self): - tv = """C7 4E BC 91 9A 5B 3B 0D D1 22 81 85 BA 02 D2 9E - F4 42 D6 9D 3D 42 76 A9 3E FE 0B F9 A1 6A 7D C0 - CD 4E AB AD AB 8C D7 A5 ED D9 66 95 F5 D3 60 AB - E0 9E 2C 65 11 A3 EC 39 7D A3 B7 6B 9E 16 74 FB""" - - btv = txt2bin(tv) - res = TurboSHAKE256.new(data=ptn(17**3)).read(64) - self.assertEqual(res, btv) - - def test_ptn_17_4(self): - tv = """02 CC 3A 88 97 E6 F4 F6 CC B6 FD 46 63 1B 1F 52 - 07 B6 6C 6D E9 C7 B5 5B 2D 1A 23 13 4A 17 0A FD - AC 23 4E AB A9 A7 7C FF 88 C1 F0 20 B7 37 24 61 - 8C 56 87 B3 62 C4 30 B2 48 CD 38 64 7F 84 8A 1D""" - - btv = txt2bin(tv) - data = ptn(17**4) - - # All at once - res = TurboSHAKE256.new(data=data).read(64) - self.assertEqual(res, btv) - - # Byte by byte - xof = TurboSHAKE256.new() - for x in data: - xof.update(bchr(x)) - res = xof.read(64) - self.assertEqual(res, btv) - - # Chunks of various prime sizes - for chunk_size in (13, 17, 19, 23, 31): - xof = TurboSHAKE256.new() - for x in chunked(data, chunk_size): - xof.update(x) - res = xof.read(64) - self.assertEqual(res, btv) - - def test_ptn_17_5(self): - tv = """AD D5 3B 06 54 3E 58 4B 58 23 F6 26 99 6A EE 50 - FE 45 ED 15 F2 02 43 A7 16 54 85 AC B4 AA 76 B4 - FF DA 75 CE DF 6D 8C DC 95 C3 32 BD 56 F4 B9 86 - B5 8B B1 7D 17 78 BF C1 B1 A9 75 45 CD F4 EC 9F""" - - btv = txt2bin(tv) - data = ptn(17**5) - - # All at once - res = TurboSHAKE256.new(data=data).read(64) - self.assertEqual(res, btv) - - # Chunks - xof = TurboSHAKE256.new() - for chunk in chunked(data, 8192): - xof.update(chunk) - res = xof.read(64) - self.assertEqual(res, btv) - - def test_ptn_17_6(self): - tv = """9E 11 BC 59 C2 4E 73 99 3C 14 84 EC 66 35 8E F7 - 1D B7 4A EF D8 4E 12 3F 78 00 BA 9C 48 53 E0 2C - FE 70 1D 9E 6B B7 65 A3 04 F0 DC 34 A4 EE 3B A8 - 2C 41 0F 0D A7 0E 86 BF BD 90 EA 87 7C 2D 61 04""" - - btv = txt2bin(tv) - data = ptn(17**6) - - res = TurboSHAKE256.new(data=data).read(64) - self.assertEqual(res, btv) - - def test_ffffff_d01(self): - tv = """D2 1C 6F BB F5 87 FA 22 82 F2 9A EA 62 01 75 FB - 02 57 41 3A F7 8A 0B 1B 2A 87 41 9C E0 31 D9 33 - AE 7A 4D 38 33 27 A8 A1 76 41 A3 4F 8A 1D 10 03 - AD 7D A6 B7 2D BA 84 BB 62 FE F2 8F 62 F1 24 24""" - - btv = txt2bin(tv) - res = TurboSHAKE256.new(data=b"\xff\xff\xff", domain=0x01).read(64) - self.assertEqual(res, btv) - - def test_ff_d06(self): - tv = """73 8D 7B 4E 37 D1 8B 7F 22 AD 1B 53 13 E3 57 E3 - DD 7D 07 05 6A 26 A3 03 C4 33 FA 35 33 45 52 80 - F4 F5 A7 D4 F7 00 EF B4 37 FE 6D 28 14 05 E0 7B - E3 2A 0A 97 2E 22 E6 3A DC 1B 09 0D AE FE 00 4B""" - - btv = txt2bin(tv) - res = TurboSHAKE256.new(data=b'\xFF', domain=0x06).read(64) - self.assertEqual(res, btv) - - def test_ffffff_d07(self): - tv = """18 B3 B5 B7 06 1C 2E 67 C1 75 3A 00 E6 AD 7E D7 - BA 1C 90 6C F9 3E FB 70 92 EA F2 7F BE EB B7 55 - AE 6E 29 24 93 C1 10 E4 8D 26 00 28 49 2B 8E 09 - B5 50 06 12 B8 F2 57 89 85 DE D5 35 7D 00 EC 67""" - - btv = txt2bin(tv) - res = TurboSHAKE256.new(data=b'\xFF' * 3, domain=0x07).read(64) - self.assertEqual(res, btv) - - def test_ffffffffffff_d0b(self): - tv = """BB 36 76 49 51 EC 97 E9 D8 5F 7E E9 A6 7A 77 18 - FC 00 5C F4 25 56 BE 79 CE 12 C0 BD E5 0E 57 36 - D6 63 2B 0D 0D FB 20 2D 1B BB 8F FE 3D D7 4C B0 - 08 34 FA 75 6C B0 34 71 BA B1 3A 1E 2C 16 B3 C0""" - - btv = txt2bin(tv) - res = TurboSHAKE256.new(data=b'\xFF' * 7, domain=0x0B).read(64) - self.assertEqual(res, btv) - - def test_ff_d30(self): - tv = """F3 FE 12 87 3D 34 BC BB 2E 60 87 79 D6 B7 0E 7F - 86 BE C7 E9 0B F1 13 CB D4 FD D0 C4 E2 F4 62 5E - 14 8D D7 EE 1A 52 77 6C F7 7F 24 05 14 D9 CC FC - 3B 5D DA B8 EE 25 5E 39 EE 38 90 72 96 2C 11 1A""" - - btv = txt2bin(tv) - res = TurboSHAKE256.new(data=b'\xFF', domain=0x30).read(64) - self.assertEqual(res, btv) - - def test_ffffff_d7f(self): - tv = """AB E5 69 C1 F7 7E C3 40 F0 27 05 E7 D3 7C 9A B7 - E1 55 51 6E 4A 6A 15 00 21 D7 0B 6F AC 0B B4 0C - 06 9F 9A 98 28 A0 D5 75 CD 99 F9 BA E4 35 AB 1A - CF 7E D9 11 0B A9 7C E0 38 8D 07 4B AC 76 87 76""" - - btv = txt2bin(tv) - res = TurboSHAKE256.new(data=b'\xFF' * 3, domain=0x7F).read(64) - self.assertEqual(res, btv) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(TurboSHAKE128Test) - tests += list_test_cases(TurboSHAKE256Test) - tests += list_test_cases(TurboSHAKE128TV) - tests += list_test_cases(TurboSHAKE256TV) - return tests - - -if __name__ == '__main__': - def suite(): - return unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_cSHAKE.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_cSHAKE.py deleted file mode 100644 index 6797160..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_cSHAKE.py +++ /dev/null @@ -1,178 +0,0 @@ -# =================================================================== -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -"""Self-test suite for Cryptodome.Hash.cSHAKE128 and cSHAKE256""" - -import unittest - -from Cryptodome.SelfTest.loader import load_test_vectors -from Cryptodome.SelfTest.st_common import list_test_cases - -from Cryptodome.Hash import cSHAKE128, cSHAKE256, SHAKE128, SHAKE256 -from Cryptodome.Util.py3compat import b, bchr, tobytes - - -class cSHAKETest(unittest.TestCase): - - def test_left_encode(self): - from Cryptodome.Hash.cSHAKE128 import _left_encode - self.assertEqual(_left_encode(0), b'\x01\x00') - self.assertEqual(_left_encode(1), b'\x01\x01') - self.assertEqual(_left_encode(256), b'\x02\x01\x00') - - def test_bytepad(self): - from Cryptodome.Hash.cSHAKE128 import _bytepad - self.assertEqual(_bytepad(b'', 4), b'\x01\x04\x00\x00') - self.assertEqual(_bytepad(b'A', 4), b'\x01\x04A\x00') - self.assertEqual(_bytepad(b'AA', 4), b'\x01\x04AA') - self.assertEqual(_bytepad(b'AAA', 4), b'\x01\x04AAA\x00\x00\x00') - self.assertEqual(_bytepad(b'AAAA', 4), b'\x01\x04AAAA\x00\x00') - self.assertEqual(_bytepad(b'AAAAA', 4), b'\x01\x04AAAAA\x00') - self.assertEqual(_bytepad(b'AAAAAA', 4), b'\x01\x04AAAAAA') - self.assertEqual(_bytepad(b'AAAAAAA', 4), b'\x01\x04AAAAAAA\x00\x00\x00') - - def test_new_positive(self): - - xof1 = self.cshake.new() - xof2 = self.cshake.new(data=b("90")) - xof3 = self.cshake.new().update(b("90")) - - self.assertNotEqual(xof1.read(10), xof2.read(10)) - xof3.read(10) - self.assertEqual(xof2.read(10), xof3.read(10)) - - xof1 = self.cshake.new() - ref = xof1.read(10) - xof2 = self.cshake.new(custom=b("")) - xof3 = self.cshake.new(custom=b("foo")) - - self.assertEqual(ref, xof2.read(10)) - self.assertNotEqual(ref, xof3.read(10)) - - xof1 = self.cshake.new(custom=b("foo")) - xof2 = self.cshake.new(custom=b("foo"), data=b("90")) - xof3 = self.cshake.new(custom=b("foo")).update(b("90")) - - self.assertNotEqual(xof1.read(10), xof2.read(10)) - xof3.read(10) - self.assertEqual(xof2.read(10), xof3.read(10)) - - def test_update(self): - pieces = [bchr(10) * 200, bchr(20) * 300] - h = self.cshake.new() - h.update(pieces[0]).update(pieces[1]) - digest = h.read(10) - h = self.cshake.new() - h.update(pieces[0] + pieces[1]) - self.assertEqual(h.read(10), digest) - - def test_update_negative(self): - h = self.cshake.new() - self.assertRaises(TypeError, h.update, u"string") - - def test_digest(self): - h = self.cshake.new() - digest = h.read(90) - - # read returns a byte string of the right length - self.assertTrue(isinstance(digest, type(b("digest")))) - self.assertEqual(len(digest), 90) - - def test_update_after_read(self): - mac = self.cshake.new() - mac.update(b("rrrr")) - mac.read(90) - self.assertRaises(TypeError, mac.update, b("ttt")) - - def test_shake(self): - # When no customization string is passed, results must match SHAKE - for digest_len in range(64): - xof1 = self.cshake.new(b'TEST') - xof2 = self.shake.new(b'TEST') - self.assertEqual(xof1.read(digest_len), xof2.read(digest_len)) - - -class cSHAKE128Test(cSHAKETest): - cshake = cSHAKE128 - shake = SHAKE128 - - -class cSHAKE256Test(cSHAKETest): - cshake = cSHAKE256 - shake = SHAKE256 - - -class cSHAKEVectors(unittest.TestCase): - pass - - -vector_files = [("ShortMsgSamples_cSHAKE128.txt", "Short Message Samples cSHAKE128", "128_cshake", cSHAKE128), - ("ShortMsgSamples_cSHAKE256.txt", "Short Message Samples cSHAKE256", "256_cshake", cSHAKE256), - ("CustomMsgSamples_cSHAKE128.txt", "Custom Message Samples cSHAKE128", "custom_128_cshake", cSHAKE128), - ("CustomMsgSamples_cSHAKE256.txt", "Custom Message Samples cSHAKE256", "custom_256_cshake", cSHAKE256), - ] - -for file, descr, tag, test_class in vector_files: - - test_vectors = load_test_vectors(("Hash", "SHA3"), file, descr, - {"len": lambda x: int(x), - "nlen": lambda x: int(x), - "slen": lambda x: int(x)}) or [] - - for idx, tv in enumerate(test_vectors): - if getattr(tv, "len", 0) == 0: - data = b("") - else: - data = tobytes(tv.msg) - assert(tv.len == len(tv.msg)*8) - if getattr(tv, "nlen", 0) != 0: - raise ValueError("Unsupported cSHAKE test vector") - if getattr(tv, "slen", 0) == 0: - custom = b("") - else: - custom = tobytes(tv.s) - assert(tv.slen == len(tv.s)*8) - - def new_test(self, data=data, result=tv.md, custom=custom, test_class=test_class): - hobj = test_class.new(data=data, custom=custom) - digest = hobj.read(len(result)) - self.assertEqual(digest, result) - - setattr(cSHAKEVectors, "test_%s_%d" % (tag, idx), new_test) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(cSHAKE128Test) - tests += list_test_cases(cSHAKE256Test) - tests += list_test_cases(cSHAKEVectors) - return tests - - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_keccak.py b/resources/lib/deps/Cryptodome/SelfTest/Hash/test_keccak.py deleted file mode 100644 index dcc0d13..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Hash/test_keccak.py +++ /dev/null @@ -1,250 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2015, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -"""Self-test suite for Cryptodome.Hash.keccak""" - -import unittest -from binascii import hexlify, unhexlify - -from Cryptodome.SelfTest.loader import load_test_vectors -from Cryptodome.SelfTest.st_common import list_test_cases - -from Cryptodome.Hash import keccak -from Cryptodome.Util.py3compat import b, tobytes, bchr - -class KeccakTest(unittest.TestCase): - - def test_new_positive(self): - - for digest_bits in (224, 256, 384, 512): - hobj = keccak.new(digest_bits=digest_bits) - self.assertEqual(hobj.digest_size, digest_bits // 8) - - hobj2 = hobj.new() - self.assertEqual(hobj2.digest_size, digest_bits // 8) - - for digest_bytes in (28, 32, 48, 64): - hobj = keccak.new(digest_bytes=digest_bytes) - self.assertEqual(hobj.digest_size, digest_bytes) - - hobj2 = hobj.new() - self.assertEqual(hobj2.digest_size, digest_bytes) - - def test_new_positive2(self): - - digest1 = keccak.new(data=b("\x90"), digest_bytes=64).digest() - digest2 = keccak.new(digest_bytes=64).update(b("\x90")).digest() - self.assertEqual(digest1, digest2) - - def test_new_negative(self): - - # keccak.new needs digest size - self.assertRaises(TypeError, keccak.new) - - h = keccak.new(digest_bits=512) - - # Either bits or bytes can be specified - self.assertRaises(TypeError, keccak.new, - digest_bytes=64, - digest_bits=512) - - # Range - self.assertRaises(ValueError, keccak.new, digest_bytes=0) - self.assertRaises(ValueError, keccak.new, digest_bytes=1) - self.assertRaises(ValueError, keccak.new, digest_bytes=65) - self.assertRaises(ValueError, keccak.new, digest_bits=0) - self.assertRaises(ValueError, keccak.new, digest_bits=1) - self.assertRaises(ValueError, keccak.new, digest_bits=513) - - def test_update(self): - pieces = [bchr(10) * 200, bchr(20) * 300] - h = keccak.new(digest_bytes=64) - h.update(pieces[0]).update(pieces[1]) - digest = h.digest() - h = keccak.new(digest_bytes=64) - h.update(pieces[0] + pieces[1]) - self.assertEqual(h.digest(), digest) - - def test_update_negative(self): - h = keccak.new(digest_bytes=64) - self.assertRaises(TypeError, h.update, u"string") - - def test_digest(self): - h = keccak.new(digest_bytes=64) - digest = h.digest() - - # hexdigest does not change the state - self.assertEqual(h.digest(), digest) - # digest returns a byte string - self.assertTrue(isinstance(digest, type(b("digest")))) - - def test_hex_digest(self): - mac = keccak.new(digest_bits=512) - digest = mac.digest() - hexdigest = mac.hexdigest() - - # hexdigest is equivalent to digest - self.assertEqual(hexlify(digest), tobytes(hexdigest)) - # hexdigest does not change the state - self.assertEqual(mac.hexdigest(), hexdigest) - # hexdigest returns a string - self.assertTrue(isinstance(hexdigest, type("digest"))) - - def test_update_after_digest(self): - msg=b("rrrrttt") - - # Normally, update() cannot be done after digest() - h = keccak.new(digest_bits=512, data=msg[:4]) - dig1 = h.digest() - self.assertRaises(TypeError, h.update, msg[4:]) - dig2 = keccak.new(digest_bits=512, data=msg).digest() - - # With the proper flag, it is allowed - h = keccak.new(digest_bits=512, data=msg[:4], update_after_digest=True) - self.assertEqual(h.digest(), dig1) - # ... and the subsequent digest applies to the entire message - # up to that point - h.update(msg[4:]) - self.assertEqual(h.digest(), dig2) - - -class KeccakVectors(unittest.TestCase): - pass - - # TODO: add ExtremelyLong tests - - -test_vectors_224 = load_test_vectors(("Hash", "keccak"), - "ShortMsgKAT_224.txt", - "Short Messages KAT 224", - {"len": lambda x: int(x)}) or [] - -test_vectors_224 += load_test_vectors(("Hash", "keccak"), - "LongMsgKAT_224.txt", - "Long Messages KAT 224", - {"len": lambda x: int(x)}) or [] - -for idx, tv in enumerate(test_vectors_224): - if tv.len == 0: - data = b("") - else: - data = tobytes(tv.msg) - - def new_test(self, data=data, result=tv.md): - hobj = keccak.new(digest_bits=224, data=data) - self.assertEqual(hobj.digest(), result) - - setattr(KeccakVectors, "test_224_%d" % idx, new_test) - -# --- - -test_vectors_256 = load_test_vectors(("Hash", "keccak"), - "ShortMsgKAT_256.txt", - "Short Messages KAT 256", - { "len" : lambda x: int(x) } ) or [] - -test_vectors_256 += load_test_vectors(("Hash", "keccak"), - "LongMsgKAT_256.txt", - "Long Messages KAT 256", - { "len" : lambda x: int(x) } ) or [] - -for idx, tv in enumerate(test_vectors_256): - if tv.len == 0: - data = b("") - else: - data = tobytes(tv.msg) - - def new_test(self, data=data, result=tv.md): - hobj = keccak.new(digest_bits=256, data=data) - self.assertEqual(hobj.digest(), result) - - setattr(KeccakVectors, "test_256_%d" % idx, new_test) - - -# --- - -test_vectors_384 = load_test_vectors(("Hash", "keccak"), - "ShortMsgKAT_384.txt", - "Short Messages KAT 384", - {"len": lambda x: int(x)}) or [] - -test_vectors_384 += load_test_vectors(("Hash", "keccak"), - "LongMsgKAT_384.txt", - "Long Messages KAT 384", - {"len": lambda x: int(x)}) or [] - -for idx, tv in enumerate(test_vectors_384): - if tv.len == 0: - data = b("") - else: - data = tobytes(tv.msg) - - def new_test(self, data=data, result=tv.md): - hobj = keccak.new(digest_bits=384, data=data) - self.assertEqual(hobj.digest(), result) - - setattr(KeccakVectors, "test_384_%d" % idx, new_test) - -# --- - -test_vectors_512 = load_test_vectors(("Hash", "keccak"), - "ShortMsgKAT_512.txt", - "Short Messages KAT 512", - {"len": lambda x: int(x)}) or [] - -test_vectors_512 += load_test_vectors(("Hash", "keccak"), - "LongMsgKAT_512.txt", - "Long Messages KAT 512", - {"len": lambda x: int(x)}) or [] - -for idx, tv in enumerate(test_vectors_512): - if tv.len == 0: - data = b("") - else: - data = tobytes(tv.msg) - - def new_test(self, data=data, result=tv.md): - hobj = keccak.new(digest_bits=512, data=data) - self.assertEqual(hobj.digest(), result) - - setattr(KeccakVectors, "test_512_%d" % idx, new_test) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(KeccakTest) - tests += list_test_cases(KeccakVectors) - return tests - - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/IO/__init__.py b/resources/lib/deps/Cryptodome/SelfTest/IO/__init__.py deleted file mode 100644 index f15f141..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/IO/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -# -# SelfTest/IO/__init__.py: Self-test for input/output module -# -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -"""Self-test for I/O""" - -def get_tests(config={}): - tests = [] - from Cryptodome.SelfTest.IO import test_PKCS8; tests += test_PKCS8.get_tests(config=config) - from Cryptodome.SelfTest.IO import test_PBES; tests += test_PBES.get_tests(config=config) - return tests - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - - diff --git a/resources/lib/deps/Cryptodome/SelfTest/IO/test_PBES.py b/resources/lib/deps/Cryptodome/SelfTest/IO/test_PBES.py deleted file mode 100644 index 19762f3..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/IO/test_PBES.py +++ /dev/null @@ -1,118 +0,0 @@ -# -# SelfTest/IO/test_PBES.py: Self-test for the _PBES module -# -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -"""Self-tests for Cryptodome.IO._PBES module""" - -import unittest - -from Cryptodome.IO._PBES import PBES2 - - -class TestPBES2(unittest.TestCase): - - def setUp(self): - self.ref = b"Test data" - self.passphrase = b"Passphrase" - - def test1(self): - ct = PBES2.encrypt(self.ref, self.passphrase, - 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC') - pt = PBES2.decrypt(ct, self.passphrase) - self.assertEqual(self.ref, pt) - - def test2(self): - ct = PBES2.encrypt(self.ref, self.passphrase, - 'PBKDF2WithHMAC-SHA224AndAES128-CBC') - pt = PBES2.decrypt(ct, self.passphrase) - self.assertEqual(self.ref, pt) - - def test3(self): - ct = PBES2.encrypt(self.ref, self.passphrase, - 'PBKDF2WithHMAC-SHA256AndAES192-CBC') - pt = PBES2.decrypt(ct, self.passphrase) - self.assertEqual(self.ref, pt) - - def test4(self): - ct = PBES2.encrypt(self.ref, self.passphrase, - 'PBKDF2WithHMAC-SHA384AndAES256-CBC') - pt = PBES2.decrypt(ct, self.passphrase) - self.assertEqual(self.ref, pt) - - def test5(self): - ct = PBES2.encrypt(self.ref, self.passphrase, - 'PBKDF2WithHMAC-SHA512AndAES128-GCM') - pt = PBES2.decrypt(ct, self.passphrase) - self.assertEqual(self.ref, pt) - - def test6(self): - ct = PBES2.encrypt(self.ref, self.passphrase, - 'PBKDF2WithHMAC-SHA512-224AndAES192-GCM') - pt = PBES2.decrypt(ct, self.passphrase) - self.assertEqual(self.ref, pt) - - def test7(self): - ct = PBES2.encrypt(self.ref, self.passphrase, - 'PBKDF2WithHMAC-SHA3-256AndAES256-GCM') - pt = PBES2.decrypt(ct, self.passphrase) - self.assertEqual(self.ref, pt) - - def test8(self): - ct = PBES2.encrypt(self.ref, self.passphrase, - 'scryptAndAES128-CBC') - pt = PBES2.decrypt(ct, self.passphrase) - self.assertEqual(self.ref, pt) - - def test9(self): - ct = PBES2.encrypt(self.ref, self.passphrase, - 'scryptAndAES192-CBC') - pt = PBES2.decrypt(ct, self.passphrase) - self.assertEqual(self.ref, pt) - - def test10(self): - ct = PBES2.encrypt(self.ref, self.passphrase, - 'scryptAndAES256-CBC') - pt = PBES2.decrypt(ct, self.passphrase) - self.assertEqual(self.ref, pt) - - -def get_tests(config={}): - from Cryptodome.SelfTest.st_common import list_test_cases - listTests = [] - listTests += list_test_cases(TestPBES2) - return listTests - - -if __name__ == '__main__': - def suite(): - return unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/IO/test_PKCS8.py b/resources/lib/deps/Cryptodome/SelfTest/IO/test_PKCS8.py deleted file mode 100644 index 718b69d..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/IO/test_PKCS8.py +++ /dev/null @@ -1,459 +0,0 @@ -# -# SelfTest/IO/test_PKCS8.py: Self-test for the PKCS8 module -# -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -"""Self-tests for Cryptodome.IO.PKCS8 module""" - -import unittest -from binascii import unhexlify - -from Cryptodome.Util.py3compat import * -from Cryptodome.IO import PKCS8 - -from Cryptodome.Util.asn1 import DerNull - -oid_key = '1.2.840.113549.1.1.1' - -# Original RSA key (in DER format) -# hexdump -v -e '32/1 "%02x" "\n"' key.der -clear_key=""" -308201ab020100025a00b94a7f7075ab9e79e8196f47be707781e80dd965cf16 -0c951a870b71783b6aaabbd550c0e65e5a3dfe15b8620009f6d7e5efec42a3f0 -6fe20faeebb0c356e79cdec6db4dd427e82d8ae4a5b90996227b8ba54ccfc4d2 -5c08050203010001025a00afa09c70d528299b7552fe766b5d20f9a221d66938 -c3b68371d48515359863ff96f0978d700e08cd6fd3d8a3f97066fc2e0d5f78eb -3a50b8e17ba297b24d1b8e9cdfd18d608668198d724ad15863ef0329195dee89 -3f039395022d0ebe0518df702a8b25954301ec60a97efdcec8eaa4f2e76ca7e8 -8dfbc3f7e0bb83f9a0e8dc47c0f8c746e9df6b022d0c9195de13f09b7be1fdd7 -1f56ae7d973e08bd9fd2c3dfd8936bb05be9cc67bd32d663c7f00d70932a0be3 -c24f022d0ac334eb6cabf1933633db007b763227b0d9971a9ea36aca8b669ec9 -4fcf16352f6b3dcae28e4bd6137db4ddd3022d0400a09f15ee7b351a2481cb03 -09920905c236d09c87afd3022f3afc2a19e3b746672b635238956ee7e6dd62d5 -022d0cd88ed14fcfbda5bbf0257f700147137bbab9c797af7df866704b889aa3 -7e2e93df3ff1a0fd3490111dcdbc4c -""" - -# Same key as above, wrapped in PKCS#8 but w/o password -# -# openssl pkcs8 -topk8 -inform DER -nocrypt -in key.der -outform DER -out keyp8.der -# hexdump -v -e '32/1 "%02x" "\n"' keyp8.der -wrapped_clear_key=""" -308201c5020100300d06092a864886f70d0101010500048201af308201ab0201 -00025a00b94a7f7075ab9e79e8196f47be707781e80dd965cf160c951a870b71 -783b6aaabbd550c0e65e5a3dfe15b8620009f6d7e5efec42a3f06fe20faeebb0 -c356e79cdec6db4dd427e82d8ae4a5b90996227b8ba54ccfc4d25c0805020301 -0001025a00afa09c70d528299b7552fe766b5d20f9a221d66938c3b68371d485 -15359863ff96f0978d700e08cd6fd3d8a3f97066fc2e0d5f78eb3a50b8e17ba2 -97b24d1b8e9cdfd18d608668198d724ad15863ef0329195dee893f039395022d -0ebe0518df702a8b25954301ec60a97efdcec8eaa4f2e76ca7e88dfbc3f7e0bb -83f9a0e8dc47c0f8c746e9df6b022d0c9195de13f09b7be1fdd71f56ae7d973e -08bd9fd2c3dfd8936bb05be9cc67bd32d663c7f00d70932a0be3c24f022d0ac3 -34eb6cabf1933633db007b763227b0d9971a9ea36aca8b669ec94fcf16352f6b -3dcae28e4bd6137db4ddd3022d0400a09f15ee7b351a2481cb0309920905c236 -d09c87afd3022f3afc2a19e3b746672b635238956ee7e6dd62d5022d0cd88ed1 -4fcfbda5bbf0257f700147137bbab9c797af7df866704b889aa37e2e93df3ff1 -a0fd3490111dcdbc4c -""" - -### -# -# The key above will now be encrypted with different algorithms. -# The password is always 'TestTest'. -# -# Each item in the wrapped_enc_keys list contains: -# * wrap algorithm -# * iteration count -# * Salt -# * IV -# * Expected result -### -wrapped_enc_keys = [] - -# -# openssl pkcs8 -topk8 -passin pass:TestTest -inform DER -in key.der -outform DER -out keyenc.der -v2 des3 -# hexdump -v -e '32/1 "%02x" "\n"' keyenc.der -# -wrapped_enc_keys.append(( -'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC', -2048, -"47EA7227D8B22E2F", # IV -"E3F7A838AB911A4D", # Salt -""" -30820216304006092a864886f70d01050d3033301b06092a864886f70d01050c -300e0408e3f7a838ab911a4d02020800301406082a864886f70d0307040847ea -7227d8b22e2f048201d0ea388b374d2d0e4ceb7a5139f850fdff274884a6e6c0 -64326e09d00dbba9018834edb5a51a6ae3d1806e6e91eebf33788ce71fee0637 -a2ebf58859dd32afc644110c390274a6128b50c39b8d907823810ec471bada86 -6f5b75d8ea04ad310fad2e73621696db8e426cd511ee93ec1714a1a7db45e036 -4bf20d178d1f16bbb250b32c2d200093169d588de65f7d99aad9ddd0104b44f1 -326962e1520dfac3c2a800e8a14f678dff2b3d0bb23f69da635bf2a643ac934e -219a447d2f4460b67149e860e54f365da130763deefa649c72b0dcd48966a2d3 -4a477444782e3e66df5a582b07bbb19778a79bd355074ce331f4a82eb966b0c4 -52a09eab6116f2722064d314ae433b3d6e81d2436e93fdf446112663cde93b87 -9c8be44beb45f18e2c78fee9b016033f01ecda51b9b142091fa69f65ab784d2c -5ad8d34be6f7f1464adfc1e0ef3f7848f40d3bdea4412758f2fcb655c93d8f4d -f6fa48fc5aa4b75dd1c017ab79ac9d737233a6d668f5364ccf47786debd37334 -9c10c9e6efbe78430a61f71c89948aa32cdc3cc7338cf994147819ce7ab23450 -c8f7d9b94c3bb377d17a3fa204b601526317824b142ff6bc843fa7815ece89c0 -839573f234dac8d80cc571a045353d61db904a4398d8ef3df5ac -""" -)) - -# -# openssl pkcs8 -topk8 -passin pass:TestTest -inform DER -in key.der -outform DER -out keyenc.der -# hexdump -v -e '32/1 "%02x" "\n"' keyenc.der -# -wrapped_enc_keys.append(( -'skip encryption', # pbeWithMD5AndDES-CBC, only decoding is supported --1, -"", -"", -""" -308201f1301b06092a864886f70d010503300e0408f9b990c89af1d41b020208 -00048201d0c6267fe8592903891933d559e71a7ca68b2e39150f19daca0f7921 -52f97e249d72f670d5140e9150433310ed7c7ee51927693fd39884cb9551cea5 -a7b746f7edf199f8787d4787a35dad930d7db057b2118851211b645ac8b90fa6 -b0e7d49ac8567cbd5fff226e87aa9129a0f52c45e9307752e8575c3b0ff756b7 -31fda6942d15ecb6b27ea19370ccc79773f47891e80d22b440d81259c4c28eac -e0ca839524116bcf52d8c566e49a95ddb0e5493437279a770a39fd333f3fca91 -55884fad0ba5aaf273121f893059d37dd417da7dcfd0d6fa7494968f13b2cc95 -65633f2c891340193e5ec00e4ee0b0e90b3b93da362a4906360845771ade1754 -9df79140be5993f3424c012598eadd3e7c7c0b4db2c72cf103d7943a5cf61420 -93370b9702386c3dd4eb0a47f34b579624a46a108b2d13921fa1b367495fe345 -6aa128aa70f8ca80ae13eb301e96c380724ce67c54380bbea2316c1faf4d058e -b4ca2e23442047606b9bc4b3bf65b432cb271bea4eb35dd3eb360d3be8612a87 -a50e96a2264490aeabdc07c6e78e5dbf4fe3388726d0e2a228346bf3c2907d68 -2a6276b22ae883fb30fa611f4e4193e7a08480fcd7db48308bacbd72bf4807aa -11fd394859f97d22982f7fe890b2e2a0f7e7ffb693 -""" -)) - -# -# openssl pkcs8 -topk8 -passin pass:TestTest -inform DER -in key.der -# -outform DER -out keyenc.der -v1 PBE-SHA1-RC2-64 -# hexdump -v -e '32/1 "%02x" "\n"' keyenc.der -# -wrapped_enc_keys.append(( -'skip encryption', # pbeWithSHA1AndRC2-CBC, only decoding is supported --1, -"", -"", -""" -308201f1301b06092a864886f70d01050b300e04083ee943bdae185008020208 -00048201d0e4614d9371d3ff10ceabc2f6a7a13a0f449f9a714144e46518ea55 -e3e6f0cde24031d01ef1f37ec40081449ef01914faf45983dde0d2bc496712de -8dd15a5527dff4721d9016c13f34fb93e3ce68577e30146266d71b539f854e56 -753a192cf126ed4812734d86f81884374f1100772f78d0646e9946407637c565 -d070acab413c55952f7237437f2e48cae7fa0ff8d370de2bf446dd08049a3663 -d9c813ac197468c02e2b687e7ca994cf7f03f01b6eca87dbfed94502c2094157 -ea39f73fe4e591df1a68b04d19d9adab90bb9898467c1464ad20bf2b8fb9a5ff -d3ec91847d1c67fd768a4b9cfb46572eccc83806601372b6fad0243f58f623b7 -1c5809dea0feb8278fe27e5560eed8448dc93f5612f546e5dd7c5f6404365eb2 -5bf3396814367ae8b15c5c432b57eaed1f882c05c7f6517ee9e42b87b7b8d071 -9d6125d1b52f7b2cca1f6bd5f584334bf90bce1a7d938274cafe27b68e629698 -b16e27ae528db28593af9adcfccbebb3b9e1f2af5cd5531b51968389caa6c091 -e7de1f1b96f0d258e54e540d961a7c0ef51fda45d6da5fddd33e9bbfd3a5f8d7 -d7ab2e971de495cddbc86d38444fee9f0ac097b00adaf7802dabe0cff5b43b45 -4f26b7b547016f89be52676866189911c53e2f2477""" -)) - -# -# openssl pkcs8 -topk8 -passin pass:TestTest -inform DER -in key.der -# -outform DER -out keyenc.der -v1 PBE-MD5-RC2-64 -# hexdump -v -e '32/1 "%02x" "\n"' keyenc.der -# -wrapped_enc_keys.append(( -'skip encryption', # pbeWithMD5AndRC2-CBC, only decoding is supported --1, -"", -"", -""" -308201f1301b06092a864886f70d010506300e0408f5cd2fee56d9b4b8020208 -00048201d086454942d6166a19d6b108465bd111e7080911f573d54b1369c676 -df28600e84936bfec04f91023ff16499e2e07178c340904f12ffa6886ab66228 -32bf43c2bff5a0ed14e765918cf5fc543ad49566246f7eb3fc044fa5a9c25f40 -8fc8c8296b91658d3bb1067c0aba008c4fefd9e2bcdbbbd63fdc8085482bccf4 -f150cec9a084259ad441a017e5d81a1034ef2484696a7a50863836d0eeda45cd -8cee8ecabfed703f8d9d4bbdf3a767d32a0ccdc38550ee2928d7fe3fa27eda5b -5c7899e75ad55d076d2c2d3c37d6da3d95236081f9671dab9a99afdb1cbc890e -332d1a91105d9a8ce08b6027aa07367bd1daec3059cb51f5d896124da16971e4 -0ca4bcadb06c854bdf39f42dd24174011414e51626d198775eff3449a982df7b -ace874e77e045eb6d7c3faef0750792b29a068a6291f7275df1123fac5789c51 -27ace42836d81633faf9daf38f6787fff0394ea484bbcd465b57d4dbee3cf8df -b77d1db287b3a6264c466805be5a4fe85cfbca180699859280f2dd8e2c2c10b5 -7a7d2ac670c6039d41952fbb0e4f99b560ebe1d020e1b96d02403283819c00cc -529c51f0b0101555e4c58002ba3c6e3c12e3fde1aec94382792e96d9666a2b33 -3dc397b22ecab67ee38a552fec29a1d4ff8719c748""" -)) - -# -# openssl pkcs8 -topk8 -passin pass:TestTest -inform DER -in key.der -# -outform DER -out keyenc.der -v1 PBE-SHA1-DES -# hexdump -v -e '32/1 "%02x" "\n"' keyenc.der -# -wrapped_enc_keys.append(( -'skip encryption', # pbeWithSHA1AndDES-CBC, only decoding is supported --1, -"", -"", -""" -308201f1301b06092a864886f70d01050a300e04089bacc9cf1e8f734e020208 -00048201d03e502f3ceafe8fd19ab2939576bfdded26d719b2441db1459688f5 -9673218b41ec1f739edf1e460bd927bc28470c87b2d4fc8ea02ba17b47a63c49 -c5c1bee40529dadfd3ef8b4472c730bc136678c78abfb34670ec9d7dcd17ee3f -892f93f2629e6e0f4b24ecb9f954069bf722f466dece3913bb6abbd2c471d9a5 -c5eea89b14aaccda43d30b0dd0f6eb6e9850d9747aa8aa8414c383ad01c374ee -26d3552abec9ba22669cc9622ccf2921e3d0c8ecd1a70e861956de0bec6104b5 -b649ac994970c83f8a9e84b14a7dff7843d4ca3dd4af87cea43b5657e15ae0b5 -a940ce5047f006ab3596506600724764f23757205fe374fee04911336d655acc -03e159ec27789191d1517c4f3f9122f5242d44d25eab8f0658cafb928566ca0e -8f6589aa0c0ab13ca7a618008ae3eafd4671ee8fe0b562e70b3623b0e2a16eee -97fd388087d2e03530c9fe7db6e52eccc7c48fd701ede35e08922861a9508d12 -bc8bbf24f0c6bee6e63dbcb489b603d4c4a78ce45bf2eab1d5d10456c42a65a8 -3a606f4e4b9b46eb13b57f2624b651859d3d2d5192b45dbd5a2ead14ff20ca76 -48f321309aa56d8c0c4a192b580821cc6c70c75e6f19d1c5414da898ec4dd39d -b0eb93d6ba387a80702dfd2db610757ba340f63230 -""" -)) - -# -# openssl pkcs8 -topk8 -passin pass:TestTest -inform DER -in key.der -# -outform DER -out keyenc.der -v2 aes128 -# hexdump -v -e '32/1 "%02x" "\n"' keyenc.der -# -wrapped_enc_keys.append(( -'PBKDF2WithHMAC-SHA1AndAES128-CBC', -2048, -"4F66EE5D3BCD531FE6EBF4B4E73016B8", # IV -"479F25156176C53A", # Salt -""" -3082021f304906092a864886f70d01050d303c301b06092a864886f70d01050c -300e0408479f25156176c53a02020800301d060960864801650304010204104f -66ee5d3bcd531fe6ebf4b4e73016b8048201d0e33cfa560423f589d097d21533 -3b880a5ebac5b2ac58b4e73b0d787aee7764f034fe34ca1d1bd845c0a7c3316f -afbfb2129e03dcaf5a5031394206492828dacef1e04639bee5935e0f46114202 -10bc6c37182f4889be11c5d0486c398f4be952e5740f65de9d8edeb275e2b406 -e19bc29ad5ebb97fa536344fc3d84c7e755696f12b810898de4e6f069b8a81c8 -0aab0d45d7d062303aaa4a10c2ce84fdb5a03114039cfe138e38bb15b2ced717 -93549cdad85e730b14d9e2198b663dfdc8d04a4349eb3de59b076ad40b116d4a -25ed917c576bc7c883c95ef0f1180e28fc9981bea069594c309f1aa1b253ceab -a2f0313bb1372bcb51a745056be93d77a1f235a762a45e8856512d436b2ca0f7 -dd60fbed394ba28978d2a2b984b028529d0a58d93aba46c6bbd4ac1e4013cbaa -63b00988bc5f11ccc40141c346762d2b28f64435d4be98ec17c1884985e3807e -e550db606600993efccf6de0dfc2d2d70b5336a3b018fa415d6bdd59f5777118 -16806b7bc17c4c7e20ad7176ebfa5a1aa3f6bc10f04b77afd443944642ac9cca -d740e082b4a3bbb8bafdd34a0b3c5f2f3c2aceccccdccd092b78994b845bfa61 -706c3b9df5165ed1dbcbf1244fe41fc9bf993f52f7658e2f87e1baaeacb0f562 -9d905c -""" -)) - -# -# openssl pkcs8 -topk8 -passin pass:TestTest -inform DER -in key.der -# -outform DER -out keyenc.der -v2 aes192 -# hexdump -v -e '32/1 "%02x" "\n"' keyenc.der -# -wrapped_enc_keys.append(( -'PBKDF2WithHMAC-SHA1AndAES192-CBC', -2048, -"5CFC2A4FF7B63201A4A8A5B021148186", # IV -"D718541C264944CE", # Salt -""" -3082021f304906092a864886f70d01050d303c301b06092a864886f70d01050c -300e0408d718541c264944ce02020800301d060960864801650304011604105c -fc2a4ff7b63201a4a8a5b021148186048201d08e74aaa21b8bcfb15b9790fe95 -b0e09ddb0f189b6fb1682fdb9f122b804650ddec3c67a1df093a828b3e5fbcc6 -286abbcc5354c482fd796d972e919ca8a5eba1eaa2293af1d648013ddad72106 -75622264dfba55dafdda39e338f058f1bdb9846041ffff803797d3fdf3693135 -8a192729ea8346a7e5e58e925a2e2e4af0818581859e8215d87370eb4194a5ff -bae900857d4c591dbc651a241865a817eaede9987c9f9ae4f95c0bf930eea88c -4d7596e535ffb7ca369988aba75027a96b9d0bc9c8b0b75f359067fd145a378b -02aaa15e9db7a23176224da48a83249005460cc6e429168657f2efa8b1af7537 -d7d7042f2d683e8271b21d591090963eeb57aea6172f88da139e1614d6a7d1a2 -1002d5a7a93d6d21156e2b4777f6fc069287a85a1538c46b7722ccde591ab55c -630e1ceeb1ac42d1b41f3f654e9da86b5efced43775ea68b2594e50e4005e052 -0fe753c0898120c2c07265367ff157f6538a1e4080d6f9d1ca9eb51939c9574e -f2e4e1e87c1434affd5808563cddd376776dbbf790c6a40028f311a8b58dafa2 -0970ed34acd6e3e89d063987893b2b9570ddb8cc032b05a723bba9444933ebf3 -c624204be72f4190e0245197d0cb772bec933fd8442445f9a28bd042d5a3a1e9 -9a8a07 -""" -)) - -# -# openssl pkcs8 -topk8 -passin pass:TestTest -inform DER -in key.der -# -outform DER -out keyenc.der -v2 aes192 -# hexdump -v -e '32/1 "%02x" "\n"' keyenc.der -# -wrapped_enc_keys.append(( -'PBKDF2WithHMAC-SHA1AndAES256-CBC', -2048, -"323351F94462AC563E053A056252C2C4", # IV -"02A6CD0D12E727B5", # Salt -""" -3082021f304906092a864886f70d01050d303c301b06092a864886f70d01050c -300e040802a6cd0d12e727b502020800301d060960864801650304012a041032 -3351f94462ac563e053a056252c2c4048201d07f4ef1c7be21aae738a20c5632 -b8bdbbb9083b6e7f68822267b1f481fd27fdafd61a90660de6e4058790e4c912 -bf3f319a7c37e6eb3d956daaa143865020d554bf6215e8d7492359aaeef45d6e -d85a686ed26c0bf7c18d071d827a86f0b73e1db0c0e7f3d42201544093302a90 -551ad530692468c47ac15c69500b8ca67d4a17b64d15cecc035ae50b768a36cf -07c395afa091e9e6f86f665455fbdc1b21ad79c0908b73da5de75a9b43508d5d -44dc97a870cd3cd9f01ca24452e9b11c1b4982946702cfcbfda5b2fcc0203fb5 -0b52a115760bd635c94d4c95ac2c640ee9a04ffaf6ccff5a8d953dd5d88ca478 -c377811c521f2191639c643d657a9e364af88bb7c14a356c2b0b4870a23c2f54 -d41f8157afff731471dccc6058b15e1151bcf84b39b5e622a3a1d65859c912a5 -591b85e034a1f6af664f030a6bfc8c3d20c70f32b54bcf4da9c2da83cef49cf8 -e9a74f0e5d358fe50b88acdce6a9db9a7ad61536212fc5f877ebfc7957b8bda4 -b1582a0f10d515a20ee06cf768db9c977aa6fbdca7540d611ff953012d009dac -e8abd059f8e8ffea637c9c7721f817aaf0bb23403e26a0ef0ff0e2037da67d41 -af728481f53443551a9bff4cea023164e9622b5441a309e1f4bff98e5bf76677 -8d7cd9 -""" -)) - -# hexdump -v -e '32/1 "%02x" "\n"' botan_scrypt.der -botan_scrypt = """ -3081f1305206092a864886f70d01050d3045302806092b06010401da47040b30 -1b040c316c5c7a847276a838a668280202200002010102010102012030190609 -60864801650304012e040c293e9bcddc0d59d64e060cb604819ab92318063480 -16148081a3123bb092b636ec0cc3b964628e181504c13eaf94987e6fb9f171d4 -9c45baeeb79c1d805d5a762d9bfd6d1995669df60a2cd0174b6d204693964de7 -05bc3fdc3a4ce5db01f30a994c82b0aac786e4f8655138c952f1cf2cc6093f90 -b5e5ca507beb539ff497b7b6370ba7f31f4928d3385dbe8bcd2395813ba1324e -6795e81a8518aff0f0a9e01396539f937b8b7b08 -""" - -# hexdump -v -e '32/1 "%02x" "\n"' botan_pbkdf2.der -botan_pbkdf2 = """ -3081f3305e06092a864886f70d01050d3051303006092a864886f70d01050c30 -23040cc91c89b368db578d2ec4c32002020fa0020118300c06082a864886f70d -02090500301d060960864801650304011604102a7147289e7c914a7d8257e4a1 -a2135b048190a648955fc96ecae56dcb4d0ab19edc5b7ef1219c88c7c3b2d0ed -b21e25d2559447f53e20b90b2f20e72456d943561c4925aad6067a4c720afb3d -691e14dfffa10ef77898e21d134f19136d35088a7aac508b296fd00d5742ad69 -8c693293b6a591e3660b130d718724d23d696f4da9bf4031475fafb682d7955c -996363f37032e10ac85afebb7cc1cbfc0e5d4c60a4c2 -""" - -def txt2bin(inputs): - s = b('').join([b(x) for x in inputs if not (x in '\n\r\t ')]) - return unhexlify(s) - -class Rng: - def __init__(self, output): - self.output=output - self.idx=0 - def __call__(self, n): - output = self.output[self.idx:self.idx+n] - self.idx += n - return output - -class PKCS8_Decrypt(unittest.TestCase): - - def setUp(self): - self.oid_key = oid_key - self.clear_key = txt2bin(clear_key) - self.wrapped_clear_key = txt2bin(wrapped_clear_key) - self.wrapped_enc_keys = [] - for t in wrapped_enc_keys: - self.wrapped_enc_keys.append(( - t[0], - t[1], - txt2bin(t[2]), - txt2bin(t[3]), - txt2bin(t[4]) - )) - - ### NO ENCRYTION - - def test1(self): - """Verify unwrapping w/o encryption""" - res1, res2, res3 = PKCS8.unwrap(self.wrapped_clear_key) - self.assertEqual(res1, self.oid_key) - self.assertEqual(res2, self.clear_key) - - def test2(self): - """Verify wrapping w/o encryption""" - wrapped = PKCS8.wrap(self.clear_key, self.oid_key) - res1, res2, res3 = PKCS8.unwrap(wrapped) - self.assertEqual(res1, self.oid_key) - self.assertEqual(res2, self.clear_key) - - ## ENCRYPTION - - def test3(self): - """Verify unwrapping with encryption""" - - for t in self.wrapped_enc_keys: - res1, res2, res3 = PKCS8.unwrap(t[4], b"TestTest") - self.assertEqual(res1, self.oid_key) - self.assertEqual(res2, self.clear_key) - - def test4(self): - """Verify wrapping with encryption""" - - for t in self.wrapped_enc_keys: - if t[0] == 'skip encryption': - continue - rng = Rng(t[2]+t[3]) - params = { 'iteration_count':t[1] } - wrapped = PKCS8.wrap( - self.clear_key, - self.oid_key, - b("TestTest"), - protection=t[0], - prot_params=params, - key_params=DerNull(), - randfunc=rng) - self.assertEqual(wrapped, t[4]) - - def test_import_botan_keys(self): - botan_scrypt_der = txt2bin(botan_scrypt) - key1 = PKCS8.unwrap(botan_scrypt_der, - b'your_password') - botan_pbkdf2_der = txt2bin(botan_pbkdf2) - key2 = PKCS8.unwrap(botan_pbkdf2_der, - b'your_password') - self.assertEqual(key1, key2) - - -def get_tests(config={}): - from Cryptodome.SelfTest.st_common import list_test_cases - listTests = [] - listTests += list_test_cases(PKCS8_Decrypt) - return listTests - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - diff --git a/resources/lib/deps/Cryptodome/SelfTest/Math/__init__.py b/resources/lib/deps/Cryptodome/SelfTest/Math/__init__.py deleted file mode 100644 index 9f732ba..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Math/__init__.py +++ /dev/null @@ -1,51 +0,0 @@ -# -# SelfTest/Math/__init__.py: Self-test for math module -# -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -"""Self-test for Math""" - -def get_tests(config={}): - tests = [] - from Cryptodome.SelfTest.Math import test_Numbers - from Cryptodome.SelfTest.Math import test_Primality - from Cryptodome.SelfTest.Math import test_modexp - from Cryptodome.SelfTest.Math import test_modmult - tests += test_Numbers.get_tests(config=config) - tests += test_Primality.get_tests(config=config) - tests += test_modexp.get_tests(config=config) - tests += test_modmult.get_tests(config=config) - return tests - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Math/test_Numbers.py b/resources/lib/deps/Cryptodome/SelfTest/Math/test_Numbers.py deleted file mode 100644 index 16a6b0b..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Math/test_Numbers.py +++ /dev/null @@ -1,825 +0,0 @@ -# -# SelfTest/Math/test_Numbers.py: Self-test for Numbers module -# -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -"""Self-test for Math.Numbers""" - -import sys -import unittest - -from Cryptodome.SelfTest.st_common import list_test_cases - -from Cryptodome.Util.py3compat import * - -from Cryptodome.Math._IntegerNative import IntegerNative - - -class TestIntegerBase(unittest.TestCase): - - def setUp(self): - raise NotImplementedError("To be implemented") - - def Integers(self, *arg): - return map(self.Integer, arg) - - def test_init_and_equality(self): - Integer = self.Integer - - v1 = Integer(23) - v2 = Integer(v1) - v3 = Integer(-9) - self.assertRaises(ValueError, Integer, 1.0) - - v4 = Integer(10**10) - v5 = Integer(-10**10) - - v6 = Integer(0xFFFF) - v7 = Integer(0xFFFFFFFF) - v8 = Integer(0xFFFFFFFFFFFFFFFF) - - self.assertEqual(v1, v1) - self.assertEqual(v1, 23) - self.assertEqual(v1, v2) - self.assertEqual(v3, -9) - self.assertEqual(v4, 10 ** 10) - self.assertEqual(v5, -10 ** 10) - self.assertEqual(v6, 0xFFFF) - self.assertEqual(v7, 0xFFFFFFFF) - self.assertEqual(v8, 0xFFFFFFFFFFFFFFFF) - - self.assertFalse(v1 == v4) - - # Init and comparison between Integer's - v6 = Integer(v1) - self.assertEqual(v1, v6) - - self.assertFalse(Integer(0) == None) - - def test_conversion_to_int(self): - v1, v2 = self.Integers(-23, 2 ** 1000) - self.assertEqual(int(v1), -23) - self.assertEqual(int(v2), 2 ** 1000) - - def test_equality_with_ints(self): - v1, v2, v3 = self.Integers(23, -89, 2 ** 1000) - self.assertTrue(v1 == 23) - self.assertTrue(v2 == -89) - self.assertFalse(v1 == 24) - self.assertTrue(v3 == 2 ** 1000) - - def test_conversion_to_str(self): - v1, v2, v3, v4 = self.Integers(20, 0, -20, 2 ** 1000) - self.assertTrue(str(v1) == "20") - self.assertTrue(str(v2) == "0") - self.assertTrue(str(v3) == "-20") - self.assertTrue(str(v4) == "10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376") - - def test_repr(self): - v1, v2 = self.Integers(-1, 2**80) - self.assertEqual(repr(v1), "Integer(-1)") - self.assertEqual(repr(v2), "Integer(1208925819614629174706176)") - - def test_conversion_to_bytes(self): - Integer = self.Integer - - v1 = Integer(0x17) - self.assertEqual(b("\x17"), v1.to_bytes()) - - v2 = Integer(0xFFFE) - self.assertEqual(b("\xFF\xFE"), v2.to_bytes()) - self.assertEqual(b("\x00\xFF\xFE"), v2.to_bytes(3)) - self.assertRaises(ValueError, v2.to_bytes, 1) - - self.assertEqual(b("\xFE\xFF"), v2.to_bytes(byteorder='little')) - self.assertEqual(b("\xFE\xFF\x00"), v2.to_bytes(3, byteorder='little')) - - v3 = Integer(-90) - self.assertRaises(ValueError, v3.to_bytes) - self.assertRaises(ValueError, v3.to_bytes, byteorder='bittle') - - def test_conversion_from_bytes(self): - Integer = self.Integer - - v1 = Integer.from_bytes(b"\x00") - self.assertTrue(isinstance(v1, Integer)) - self.assertEqual(0, v1) - - v2 = Integer.from_bytes(b"\x00\x01") - self.assertEqual(1, v2) - - v3 = Integer.from_bytes(b"\xFF\xFF") - self.assertEqual(0xFFFF, v3) - - v4 = Integer.from_bytes(b"\x00\x01", 'big') - self.assertEqual(1, v4) - - v5 = Integer.from_bytes(b"\x00\x01", byteorder='big') - self.assertEqual(1, v5) - - v6 = Integer.from_bytes(b"\x00\x01", byteorder='little') - self.assertEqual(0x0100, v6) - - self.assertRaises(ValueError, Integer.from_bytes, b'\x09', 'bittle') - - def test_inequality(self): - # Test Integer!=Integer and Integer!=int - v1, v2, v3, v4 = self.Integers(89, 89, 90, -8) - self.assertTrue(v1 != v3) - self.assertTrue(v1 != 90) - self.assertFalse(v1 != v2) - self.assertFalse(v1 != 89) - self.assertTrue(v1 != v4) - self.assertTrue(v4 != v1) - self.assertTrue(self.Integer(0) != None) - - def test_less_than(self): - # Test IntegerInteger and Integer>int - v1, v2, v3, v4, v5 = self.Integers(13, 13, 14, -8, 2 ** 10) - self.assertTrue(v3 > v1) - self.assertTrue(v3 > 13) - self.assertFalse(v1 > v1) - self.assertFalse(v1 > v2) - self.assertFalse(v1 > 13) - self.assertTrue(v1 > v4) - self.assertFalse(v4 > v1) - self.assertTrue(v5 > v1) - self.assertFalse(v1 > v5) - - def test_more_than_or_equal(self): - # Test Integer>=Integer and Integer>=int - v1, v2, v3, v4 = self.Integers(13, 13, 14, -4) - self.assertTrue(v3 >= v1) - self.assertTrue(v3 >= 13) - self.assertTrue(v1 >= v2) - self.assertTrue(v1 >= v1) - self.assertTrue(v1 >= 13) - self.assertFalse(v4 >= v1) - - def test_bool(self): - v1, v2, v3, v4 = self.Integers(0, 10, -9, 2 ** 10) - self.assertFalse(v1) - self.assertFalse(bool(v1)) - self.assertTrue(v2) - self.assertTrue(bool(v2)) - self.assertTrue(v3) - self.assertTrue(v4) - - def test_is_negative(self): - v1, v2, v3, v4, v5 = self.Integers(-3 ** 100, -3, 0, 3, 3**100) - self.assertTrue(v1.is_negative()) - self.assertTrue(v2.is_negative()) - self.assertFalse(v4.is_negative()) - self.assertFalse(v5.is_negative()) - - def test_addition(self): - # Test Integer+Integer and Integer+int - v1, v2, v3 = self.Integers(7, 90, -7) - self.assertTrue(isinstance(v1 + v2, self.Integer)) - self.assertEqual(v1 + v2, 97) - self.assertEqual(v1 + 90, 97) - self.assertEqual(v1 + v3, 0) - self.assertEqual(v1 + (-7), 0) - self.assertEqual(v1 + 2 ** 10, 2 ** 10 + 7) - - def test_subtraction(self): - # Test Integer-Integer and Integer-int - v1, v2, v3 = self.Integers(7, 90, -7) - self.assertTrue(isinstance(v1 - v2, self.Integer)) - self.assertEqual(v2 - v1, 83) - self.assertEqual(v2 - 7, 83) - self.assertEqual(v2 - v3, 97) - self.assertEqual(v1 - (-7), 14) - self.assertEqual(v1 - 2 ** 10, 7 - 2 ** 10) - - def test_multiplication(self): - # Test Integer-Integer and Integer-int - v1, v2, v3, v4 = self.Integers(4, 5, -2, 2 ** 10) - self.assertTrue(isinstance(v1 * v2, self.Integer)) - self.assertEqual(v1 * v2, 20) - self.assertEqual(v1 * 5, 20) - self.assertEqual(v1 * -2, -8) - self.assertEqual(v1 * 2 ** 10, 4 * (2 ** 10)) - - def test_floor_div(self): - v1, v2, v3 = self.Integers(3, 8, 2 ** 80) - self.assertTrue(isinstance(v1 // v2, self.Integer)) - self.assertEqual(v2 // v1, 2) - self.assertEqual(v2 // 3, 2) - self.assertEqual(v2 // -3, -3) - self.assertEqual(v3 // 2 ** 79, 2) - self.assertRaises(ZeroDivisionError, lambda: v1 // 0) - - def test_remainder(self): - # Test Integer%Integer and Integer%int - v1, v2, v3 = self.Integers(23, 5, -4) - self.assertTrue(isinstance(v1 % v2, self.Integer)) - self.assertEqual(v1 % v2, 3) - self.assertEqual(v1 % 5, 3) - self.assertEqual(v3 % 5, 1) - self.assertEqual(v1 % 2 ** 10, 23) - self.assertRaises(ZeroDivisionError, lambda: v1 % 0) - self.assertRaises(ValueError, lambda: v1 % -6) - - def test_simple_exponentiation(self): - v1, v2, v3 = self.Integers(4, 3, -2) - self.assertTrue(isinstance(v1 ** v2, self.Integer)) - self.assertEqual(v1 ** v2, 64) - self.assertEqual(pow(v1, v2), 64) - self.assertEqual(v1 ** 3, 64) - self.assertEqual(pow(v1, 3), 64) - self.assertEqual(v3 ** 2, 4) - self.assertEqual(v3 ** 3, -8) - - self.assertRaises(ValueError, pow, v1, -3) - - def test_modular_exponentiation(self): - v1, v2, v3 = self.Integers(23, 5, 17) - - self.assertTrue(isinstance(pow(v1, v2, v3), self.Integer)) - self.assertEqual(pow(v1, v2, v3), 7) - self.assertEqual(pow(v1, 5, v3), 7) - self.assertEqual(pow(v1, v2, 17), 7) - self.assertEqual(pow(v1, 5, 17), 7) - self.assertEqual(pow(v1, 0, 17), 1) - self.assertEqual(pow(v1, 1, 2 ** 80), 23) - self.assertEqual(pow(v1, 2 ** 80, 89298), 17689) - - self.assertRaises(ZeroDivisionError, pow, v1, 5, 0) - self.assertRaises(ValueError, pow, v1, 5, -4) - self.assertRaises(ValueError, pow, v1, -3, 8) - - def test_inplace_exponentiation(self): - v1 = self.Integer(4) - v1.inplace_pow(2) - self.assertEqual(v1, 16) - - v1 = self.Integer(4) - v1.inplace_pow(2, 15) - self.assertEqual(v1, 1) - - def test_abs(self): - v1, v2, v3, v4, v5 = self.Integers(-2 ** 100, -2, 0, 2, 2 ** 100) - self.assertEqual(abs(v1), 2 ** 100) - self.assertEqual(abs(v2), 2) - self.assertEqual(abs(v3), 0) - self.assertEqual(abs(v4), 2) - self.assertEqual(abs(v5), 2 ** 100) - - def test_sqrt(self): - v1, v2, v3, v4 = self.Integers(-2, 0, 49, 10**100) - - self.assertRaises(ValueError, v1.sqrt) - self.assertEqual(v2.sqrt(), 0) - self.assertEqual(v3.sqrt(), 7) - self.assertEqual(v4.sqrt(), 10**50) - - def test_sqrt_module(self): - - # Invalid modulus (non positive) - self.assertRaises(ValueError, self.Integer(5).sqrt, 0) - self.assertRaises(ValueError, self.Integer(5).sqrt, -1) - - # Simple cases - assert self.Integer(0).sqrt(5) == 0 - assert self.Integer(1).sqrt(5) in (1, 4) - - # Test with all quadratic residues in several fields - for p in (11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53): - for i in range(0, p): - square = i**2 % p - res = self.Integer(square).sqrt(p) - assert res in (i, p - i) - - # 2 is a non-quadratic reside in Z_11 - self.assertRaises(ValueError, self.Integer(2).sqrt, 11) - - # 10 is not a prime - self.assertRaises(ValueError, self.Integer(4).sqrt, 10) - - # 5 is square residue of 4 and 7 - assert self.Integer(5 - 11).sqrt(11) in (4, 7) - assert self.Integer(5 + 11).sqrt(11) in (4, 7) - - def test_in_place_add(self): - v1, v2 = self.Integers(10, 20) - - v1 += v2 - self.assertEqual(v1, 30) - v1 += 10 - self.assertEqual(v1, 40) - v1 += -1 - self.assertEqual(v1, 39) - v1 += 2 ** 1000 - self.assertEqual(v1, 39 + 2 ** 1000) - - def test_in_place_sub(self): - v1, v2 = self.Integers(10, 20) - - v1 -= v2 - self.assertEqual(v1, -10) - v1 -= -100 - self.assertEqual(v1, 90) - v1 -= 90000 - self.assertEqual(v1, -89910) - v1 -= -100000 - self.assertEqual(v1, 10090) - - def test_in_place_mul(self): - v1, v2 = self.Integers(3, 5) - - v1 *= v2 - self.assertEqual(v1, 15) - v1 *= 2 - self.assertEqual(v1, 30) - v1 *= -2 - self.assertEqual(v1, -60) - v1 *= 2 ** 1000 - self.assertEqual(v1, -60 * (2 ** 1000)) - - def test_in_place_modulus(self): - v1, v2 = self.Integers(20, 7) - - v1 %= v2 - self.assertEqual(v1, 6) - v1 %= 2 ** 1000 - self.assertEqual(v1, 6) - v1 %= 2 - self.assertEqual(v1, 0) - def t(): - v3 = self.Integer(9) - v3 %= 0 - self.assertRaises(ZeroDivisionError, t) - - def test_and(self): - v1, v2, v3 = self.Integers(0xF4, 0x31, -0xF) - self.assertTrue(isinstance(v1 & v2, self.Integer)) - self.assertEqual(v1 & v2, 0x30) - self.assertEqual(v1 & 0x31, 0x30) - self.assertEqual(v1 & v3, 0xF0) - self.assertEqual(v1 & -0xF, 0xF0) - self.assertEqual(v3 & -0xF, -0xF) - self.assertEqual(v2 & (2 ** 1000 + 0x31), 0x31) - - def test_or(self): - v1, v2, v3 = self.Integers(0x40, 0x82, -0xF) - self.assertTrue(isinstance(v1 | v2, self.Integer)) - self.assertEqual(v1 | v2, 0xC2) - self.assertEqual(v1 | 0x82, 0xC2) - self.assertEqual(v2 | v3, -0xD) - self.assertEqual(v2 | 2 ** 1000, 2 ** 1000 + 0x82) - - def test_right_shift(self): - v1, v2, v3 = self.Integers(0x10, 1, -0x10) - self.assertEqual(v1 >> 0, v1) - self.assertTrue(isinstance(v1 >> v2, self.Integer)) - self.assertEqual(v1 >> v2, 0x08) - self.assertEqual(v1 >> 1, 0x08) - self.assertRaises(ValueError, lambda: v1 >> -1) - self.assertEqual(v1 >> (2 ** 1000), 0) - - self.assertEqual(v3 >> 1, -0x08) - self.assertEqual(v3 >> (2 ** 1000), -1) - - def test_in_place_right_shift(self): - v1, v2, v3 = self.Integers(0x10, 1, -0x10) - v1 >>= 0 - self.assertEqual(v1, 0x10) - v1 >>= 1 - self.assertEqual(v1, 0x08) - v1 >>= v2 - self.assertEqual(v1, 0x04) - v3 >>= 1 - self.assertEqual(v3, -0x08) - def l(): - v4 = self.Integer(0x90) - v4 >>= -1 - self.assertRaises(ValueError, l) - def m1(): - v4 = self.Integer(0x90) - v4 >>= 2 ** 1000 - return v4 - self.assertEqual(0, m1()) - def m2(): - v4 = self.Integer(-1) - v4 >>= 2 ** 1000 - return v4 - self.assertEqual(-1, m2()) - - def _test_left_shift(self): - v1, v2, v3 = self.Integers(0x10, 1, -0x10) - self.assertEqual(v1 << 0, v1) - self.assertTrue(isinstance(v1 << v2, self.Integer)) - self.assertEqual(v1 << v2, 0x20) - self.assertEqual(v1 << 1, 0x20) - self.assertEqual(v3 << 1, -0x20) - self.assertRaises(ValueError, lambda: v1 << -1) - self.assertRaises(ValueError, lambda: v1 << (2 ** 1000)) - - def test_in_place_left_shift(self): - v1, v2, v3 = self.Integers(0x10, 1, -0x10) - v1 <<= 0 - self.assertEqual(v1, 0x10) - v1 <<= 1 - self.assertEqual(v1, 0x20) - v1 <<= v2 - self.assertEqual(v1, 0x40) - v3 <<= 1 - self.assertEqual(v3, -0x20) - def l(): - v4 = self.Integer(0x90) - v4 <<= -1 - self.assertRaises(ValueError, l) - def m(): - v4 = self.Integer(0x90) - v4 <<= 2 ** 1000 - self.assertRaises(ValueError, m) - - - def test_get_bit(self): - v1, v2, v3 = self.Integers(0x102, -3, 1) - self.assertEqual(v1.get_bit(0), 0) - self.assertEqual(v1.get_bit(1), 1) - self.assertEqual(v1.get_bit(v3), 1) - self.assertEqual(v1.get_bit(8), 1) - self.assertEqual(v1.get_bit(9), 0) - - self.assertRaises(ValueError, v1.get_bit, -1) - self.assertEqual(v1.get_bit(2 ** 1000), 0) - - self.assertRaises(ValueError, v2.get_bit, -1) - self.assertRaises(ValueError, v2.get_bit, 0) - self.assertRaises(ValueError, v2.get_bit, 1) - self.assertRaises(ValueError, v2.get_bit, 2 * 1000) - - def test_odd_even(self): - v1, v2, v3, v4, v5 = self.Integers(0, 4, 17, -4, -17) - - self.assertTrue(v1.is_even()) - self.assertTrue(v2.is_even()) - self.assertFalse(v3.is_even()) - self.assertTrue(v4.is_even()) - self.assertFalse(v5.is_even()) - - self.assertFalse(v1.is_odd()) - self.assertFalse(v2.is_odd()) - self.assertTrue(v3.is_odd()) - self.assertFalse(v4.is_odd()) - self.assertTrue(v5.is_odd()) - - def test_size_in_bits(self): - v1, v2, v3, v4 = self.Integers(0, 1, 0x100, -90) - self.assertEqual(v1.size_in_bits(), 1) - self.assertEqual(v2.size_in_bits(), 1) - self.assertEqual(v3.size_in_bits(), 9) - self.assertRaises(ValueError, v4.size_in_bits) - - def test_size_in_bytes(self): - v1, v2, v3, v4, v5, v6 = self.Integers(0, 1, 0xFF, 0x1FF, 0x10000, -9) - self.assertEqual(v1.size_in_bytes(), 1) - self.assertEqual(v2.size_in_bytes(), 1) - self.assertEqual(v3.size_in_bytes(), 1) - self.assertEqual(v4.size_in_bytes(), 2) - self.assertEqual(v5.size_in_bytes(), 3) - self.assertRaises(ValueError, v6.size_in_bits) - - def test_perfect_square(self): - - self.assertFalse(self.Integer(-9).is_perfect_square()) - self.assertTrue(self.Integer(0).is_perfect_square()) - self.assertTrue(self.Integer(1).is_perfect_square()) - self.assertFalse(self.Integer(2).is_perfect_square()) - self.assertFalse(self.Integer(3).is_perfect_square()) - self.assertTrue(self.Integer(4).is_perfect_square()) - self.assertTrue(self.Integer(39*39).is_perfect_square()) - self.assertFalse(self.Integer(39*39+1).is_perfect_square()) - - for x in range(100, 1000): - self.assertFalse(self.Integer(x**2+1).is_perfect_square()) - self.assertTrue(self.Integer(x**2).is_perfect_square()) - - def test_fail_if_divisible_by(self): - v1, v2, v3 = self.Integers(12, -12, 4) - - # No failure expected - v1.fail_if_divisible_by(7) - v2.fail_if_divisible_by(7) - v2.fail_if_divisible_by(2 ** 80) - - # Failure expected - self.assertRaises(ValueError, v1.fail_if_divisible_by, 4) - self.assertRaises(ValueError, v1.fail_if_divisible_by, v3) - - def test_multiply_accumulate(self): - v1, v2, v3 = self.Integers(4, 3, 2) - v1.multiply_accumulate(v2, v3) - self.assertEqual(v1, 10) - v1.multiply_accumulate(v2, 2) - self.assertEqual(v1, 16) - v1.multiply_accumulate(3, v3) - self.assertEqual(v1, 22) - v1.multiply_accumulate(1, -2) - self.assertEqual(v1, 20) - v1.multiply_accumulate(-2, 1) - self.assertEqual(v1, 18) - v1.multiply_accumulate(1, 2 ** 1000) - self.assertEqual(v1, 18 + 2 ** 1000) - v1.multiply_accumulate(2 ** 1000, 1) - self.assertEqual(v1, 18 + 2 ** 1001) - - def test_set(self): - v1, v2 = self.Integers(3, 6) - v1.set(v2) - self.assertEqual(v1, 6) - v1.set(9) - self.assertEqual(v1, 9) - v1.set(-2) - self.assertEqual(v1, -2) - v1.set(2 ** 1000) - self.assertEqual(v1, 2 ** 1000) - - def test_inverse(self): - v1, v2, v3, v4, v5, v6 = self.Integers(2, 5, -3, 0, 723872, 3433) - - self.assertTrue(isinstance(v1.inverse(v2), self.Integer)) - self.assertEqual(v1.inverse(v2), 3) - self.assertEqual(v1.inverse(5), 3) - self.assertEqual(v3.inverse(5), 3) - self.assertEqual(v5.inverse(92929921), 58610507) - self.assertEqual(v6.inverse(9912), 5353) - - self.assertRaises(ValueError, v2.inverse, 10) - self.assertRaises(ValueError, v1.inverse, -3) - self.assertRaises(ValueError, v4.inverse, 10) - self.assertRaises(ZeroDivisionError, v2.inverse, 0) - - def test_inplace_inverse(self): - v1, v2 = self.Integers(2, 5) - - v1.inplace_inverse(v2) - self.assertEqual(v1, 3) - - def test_gcd(self): - v1, v2, v3, v4 = self.Integers(6, 10, 17, -2) - self.assertTrue(isinstance(v1.gcd(v2), self.Integer)) - self.assertEqual(v1.gcd(v2), 2) - self.assertEqual(v1.gcd(10), 2) - self.assertEqual(v1.gcd(v3), 1) - self.assertEqual(v1.gcd(-2), 2) - self.assertEqual(v4.gcd(6), 2) - - def test_lcm(self): - v1, v2, v3, v4, v5 = self.Integers(6, 10, 17, -2, 0) - self.assertTrue(isinstance(v1.lcm(v2), self.Integer)) - self.assertEqual(v1.lcm(v2), 30) - self.assertEqual(v1.lcm(10), 30) - self.assertEqual(v1.lcm(v3), 102) - self.assertEqual(v1.lcm(-2), 6) - self.assertEqual(v4.lcm(6), 6) - self.assertEqual(v1.lcm(0), 0) - self.assertEqual(v5.lcm(0), 0) - - def test_jacobi_symbol(self): - - data = ( - (1001, 1, 1), - (19, 45, 1), - (8, 21, -1), - (5, 21, 1), - (610, 987, -1), - (1001, 9907, -1), - (5, 3439601197, -1) - ) - - js = self.Integer.jacobi_symbol - - # Jacobi symbol is always 1 for k==1 or n==1 - for k in range(1, 30): - self.assertEqual(js(k, 1), 1) - for n in range(1, 30, 2): - self.assertEqual(js(1, n), 1) - - # Fail if n is not positive odd - self.assertRaises(ValueError, js, 6, -2) - self.assertRaises(ValueError, js, 6, -1) - self.assertRaises(ValueError, js, 6, 0) - self.assertRaises(ValueError, js, 0, 0) - self.assertRaises(ValueError, js, 6, 2) - self.assertRaises(ValueError, js, 6, 4) - self.assertRaises(ValueError, js, 6, 6) - self.assertRaises(ValueError, js, 6, 8) - - for tv in data: - self.assertEqual(js(tv[0], tv[1]), tv[2]) - self.assertEqual(js(self.Integer(tv[0]), tv[1]), tv[2]) - self.assertEqual(js(tv[0], self.Integer(tv[1])), tv[2]) - - def test_jacobi_symbol_wikipedia(self): - - # Test vectors from https://en.wikipedia.org/wiki/Jacobi_symbol - tv = [ - (3, [(1, 1), (2, -1), (3, 0), (4, 1), (5, -1), (6, 0), (7, 1), (8, -1), (9, 0), (10, 1), (11, -1), (12, 0), (13, 1), (14, -1), (15, 0), (16, 1), (17, -1), (18, 0), (19, 1), (20, -1), (21, 0), (22, 1), (23, -1), (24, 0), (25, 1), (26, -1), (27, 0), (28, 1), (29, -1), (30, 0)]), - (5, [(1, 1), (2, -1), (3, -1), (4, 1), (5, 0), (6, 1), (7, -1), (8, -1), (9, 1), (10, 0), (11, 1), (12, -1), (13, -1), (14, 1), (15, 0), (16, 1), (17, -1), (18, -1), (19, 1), (20, 0), (21, 1), (22, -1), (23, -1), (24, 1), (25, 0), (26, 1), (27, -1), (28, -1), (29, 1), (30, 0)]), - (7, [(1, 1), (2, 1), (3, -1), (4, 1), (5, -1), (6, -1), (7, 0), (8, 1), (9, 1), (10, -1), (11, 1), (12, -1), (13, -1), (14, 0), (15, 1), (16, 1), (17, -1), (18, 1), (19, -1), (20, -1), (21, 0), (22, 1), (23, 1), (24, -1), (25, 1), (26, -1), (27, -1), (28, 0), (29, 1), (30, 1)]), - (9, [(1, 1), (2, 1), (3, 0), (4, 1), (5, 1), (6, 0), (7, 1), (8, 1), (9, 0), (10, 1), (11, 1), (12, 0), (13, 1), (14, 1), (15, 0), (16, 1), (17, 1), (18, 0), (19, 1), (20, 1), (21, 0), (22, 1), (23, 1), (24, 0), (25, 1), (26, 1), (27, 0), (28, 1), (29, 1), (30, 0)]), - (11, [(1, 1), (2, -1), (3, 1), (4, 1), (5, 1), (6, -1), (7, -1), (8, -1), (9, 1), (10, -1), (11, 0), (12, 1), (13, -1), (14, 1), (15, 1), (16, 1), (17, -1), (18, -1), (19, -1), (20, 1), (21, -1), (22, 0), (23, 1), (24, -1), (25, 1), (26, 1), (27, 1), (28, -1), (29, -1), (30, -1)]), - (13, [(1, 1), (2, -1), (3, 1), (4, 1), (5, -1), (6, -1), (7, -1), (8, -1), (9, 1), (10, 1), (11, -1), (12, 1), (13, 0), (14, 1), (15, -1), (16, 1), (17, 1), (18, -1), (19, -1), (20, -1), (21, -1), (22, 1), (23, 1), (24, -1), (25, 1), (26, 0), (27, 1), (28, -1), (29, 1), (30, 1)]), - (15, [(1, 1), (2, 1), (3, 0), (4, 1), (5, 0), (6, 0), (7, -1), (8, 1), (9, 0), (10, 0), (11, -1), (12, 0), (13, -1), (14, -1), (15, 0), (16, 1), (17, 1), (18, 0), (19, 1), (20, 0), (21, 0), (22, -1), (23, 1), (24, 0), (25, 0), (26, -1), (27, 0), (28, -1), (29, -1), (30, 0)]), - (17, [(1, 1), (2, 1), (3, -1), (4, 1), (5, -1), (6, -1), (7, -1), (8, 1), (9, 1), (10, -1), (11, -1), (12, -1), (13, 1), (14, -1), (15, 1), (16, 1), (17, 0), (18, 1), (19, 1), (20, -1), (21, 1), (22, -1), (23, -1), (24, -1), (25, 1), (26, 1), (27, -1), (28, -1), (29, -1), (30, 1)]), - (19, [(1, 1), (2, -1), (3, -1), (4, 1), (5, 1), (6, 1), (7, 1), (8, -1), (9, 1), (10, -1), (11, 1), (12, -1), (13, -1), (14, -1), (15, -1), (16, 1), (17, 1), (18, -1), (19, 0), (20, 1), (21, -1), (22, -1), (23, 1), (24, 1), (25, 1), (26, 1), (27, -1), (28, 1), (29, -1), (30, 1)]), - (21, [(1, 1), (2, -1), (3, 0), (4, 1), (5, 1), (6, 0), (7, 0), (8, -1), (9, 0), (10, -1), (11, -1), (12, 0), (13, -1), (14, 0), (15, 0), (16, 1), (17, 1), (18, 0), (19, -1), (20, 1), (21, 0), (22, 1), (23, -1), (24, 0), (25, 1), (26, 1), (27, 0), (28, 0), (29, -1), (30, 0)]), - (23, [(1, 1), (2, 1), (3, 1), (4, 1), (5, -1), (6, 1), (7, -1), (8, 1), (9, 1), (10, -1), (11, -1), (12, 1), (13, 1), (14, -1), (15, -1), (16, 1), (17, -1), (18, 1), (19, -1), (20, -1), (21, -1), (22, -1), (23, 0), (24, 1), (25, 1), (26, 1), (27, 1), (28, -1), (29, 1), (30, -1)]), - (25, [(1, 1), (2, 1), (3, 1), (4, 1), (5, 0), (6, 1), (7, 1), (8, 1), (9, 1), (10, 0), (11, 1), (12, 1), (13, 1), (14, 1), (15, 0), (16, 1), (17, 1), (18, 1), (19, 1), (20, 0), (21, 1), (22, 1), (23, 1), (24, 1), (25, 0), (26, 1), (27, 1), (28, 1), (29, 1), (30, 0)]), - (27, [(1, 1), (2, -1), (3, 0), (4, 1), (5, -1), (6, 0), (7, 1), (8, -1), (9, 0), (10, 1), (11, -1), (12, 0), (13, 1), (14, -1), (15, 0), (16, 1), (17, -1), (18, 0), (19, 1), (20, -1), (21, 0), (22, 1), (23, -1), (24, 0), (25, 1), (26, -1), (27, 0), (28, 1), (29, -1), (30, 0)]), - (29, [(1, 1), (2, -1), (3, -1), (4, 1), (5, 1), (6, 1), (7, 1), (8, -1), (9, 1), (10, -1), (11, -1), (12, -1), (13, 1), (14, -1), (15, -1), (16, 1), (17, -1), (18, -1), (19, -1), (20, 1), (21, -1), (22, 1), (23, 1), (24, 1), (25, 1), (26, -1), (27, -1), (28, 1), (29, 0), (30, 1)]), - ] - - js = self.Integer.jacobi_symbol - - for n, kj in tv: - for k, j in kj: - self.assertEqual(js(k, n), j) - - def test_hex(self): - v1, = self.Integers(0x10) - self.assertEqual(hex(v1), "0x10") - - def test_mult_modulo_bytes(self): - modmult = self.Integer._mult_modulo_bytes - - res = modmult(4, 5, 19) - self.assertEqual(res, b'\x01') - - res = modmult(4 - 19, 5, 19) - self.assertEqual(res, b'\x01') - - res = modmult(4, 5 - 19, 19) - self.assertEqual(res, b'\x01') - - res = modmult(4 + 19, 5, 19) - self.assertEqual(res, b'\x01') - - res = modmult(4, 5 + 19, 19) - self.assertEqual(res, b'\x01') - - modulus = 2**512 - 1 # 64 bytes - t1 = 13**100 - t2 = 17**100 - expect = b"\xfa\xb2\x11\x87\xc3(y\x07\xf8\xf1n\xdepq\x0b\xca\xf3\xd3B,\xef\xf2\xfbf\xcc)\x8dZ*\x95\x98r\x96\xa8\xd5\xc3}\xe2q:\xa2'z\xf48\xde%\xef\t\x07\xbc\xc4[C\x8bUE2\x90\xef\x81\xaa:\x08" - self.assertEqual(expect, modmult(t1, t2, modulus)) - - self.assertRaises(ZeroDivisionError, modmult, 4, 5, 0) - self.assertRaises(ValueError, modmult, 4, 5, -1) - self.assertRaises(ValueError, modmult, 4, 5, 4) - - -class TestIntegerInt(TestIntegerBase): - - def setUp(self): - self.Integer = IntegerNative - - -class testIntegerRandom(unittest.TestCase): - - def test_random_exact_bits(self): - - for _ in range(1000): - a = IntegerNative.random(exact_bits=8) - self.assertFalse(a < 128) - self.assertFalse(a >= 256) - - for bits_value in range(1024, 1024 + 8): - a = IntegerNative.random(exact_bits=bits_value) - self.assertFalse(a < 2**(bits_value - 1)) - self.assertFalse(a >= 2**bits_value) - - def test_random_max_bits(self): - - flag = False - for _ in range(1000): - a = IntegerNative.random(max_bits=8) - flag = flag or a < 128 - self.assertFalse(a>=256) - self.assertTrue(flag) - - for bits_value in range(1024, 1024 + 8): - a = IntegerNative.random(max_bits=bits_value) - self.assertFalse(a >= 2**bits_value) - - def test_random_bits_custom_rng(self): - - class CustomRNG(object): - def __init__(self): - self.counter = 0 - - def __call__(self, size): - self.counter += size - return bchr(0) * size - - custom_rng = CustomRNG() - a = IntegerNative.random(exact_bits=32, randfunc=custom_rng) - self.assertEqual(custom_rng.counter, 4) - - def test_random_range(self): - - func = IntegerNative.random_range - - for x in range(200): - a = func(min_inclusive=1, max_inclusive=15) - self.assertTrue(1 <= a <= 15) - - for x in range(200): - a = func(min_inclusive=1, max_exclusive=15) - self.assertTrue(1 <= a < 15) - - self.assertRaises(ValueError, func, min_inclusive=1, max_inclusive=2, - max_exclusive=3) - self.assertRaises(ValueError, func, max_inclusive=2, max_exclusive=3) - -def get_tests(config={}): - tests = [] - tests += list_test_cases(TestIntegerInt) - - try: - from Cryptodome.Math._IntegerGMP import IntegerGMP - - class TestIntegerGMP(TestIntegerBase): - def setUp(self): - self.Integer = IntegerGMP - - tests += list_test_cases(TestIntegerGMP) - except (ImportError, OSError) as e: - if sys.platform == "win32": - sys.stdout.write("Skipping GMP tests on Windows\n") - else: - sys.stdout.write("Skipping GMP tests (%s)\n" % str(e) ) - - try: - from Cryptodome.Math._IntegerCustom import IntegerCustom - - class TestIntegerCustomModexp(TestIntegerBase): - def setUp(self): - self.Integer = IntegerCustom - - tests += list_test_cases(TestIntegerCustomModexp) - except (ImportError, OSError) as e: - sys.stdout.write("Skipping custom modexp tests (%s)\n" % str(e) ) - - tests += list_test_cases(testIntegerRandom) - return tests - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Math/test_Primality.py b/resources/lib/deps/Cryptodome/SelfTest/Math/test_Primality.py deleted file mode 100644 index 475d1d4..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Math/test_Primality.py +++ /dev/null @@ -1,118 +0,0 @@ -# -# SelfTest/Math/test_Primality.py: Self-test for Primality module -# -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -"""Self-test for Math.Numbers""" - -import unittest - -from Cryptodome.SelfTest.st_common import list_test_cases - -from Cryptodome.Util.py3compat import * - -from Cryptodome.Math.Numbers import Integer -from Cryptodome.Math.Primality import ( - PROBABLY_PRIME, COMPOSITE, - miller_rabin_test, lucas_test, - test_probable_prime, - generate_probable_prime, - generate_probable_safe_prime, - ) - - -class TestPrimality(unittest.TestCase): - - primes = (1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 2**127-1, 175637383534939453397801320455508570374088202376942372758907369518414308188137781042871856139027160010343454418881888953150175357127346872102307696660678617989191485418582475696230580407111841072614783095326672517315988762029036079794994990250662362650625650262324085116467511357592728695033227611029693067539) - composites = (0, 4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 7*23, (2**19-1)*(2**67-1), 9746347772161,) - - def test_miller_rabin(self): - for prime in self.primes: - self.assertEqual(miller_rabin_test(prime, 3), PROBABLY_PRIME) - for composite in self.composites: - self.assertEqual(miller_rabin_test(composite, 3), COMPOSITE) - self.assertRaises(ValueError, miller_rabin_test, -1, 3) - - def test_lucas(self): - for prime in self.primes: - res = lucas_test(prime) - self.assertEqual(res, PROBABLY_PRIME) - for composite in self.composites: - res = lucas_test(composite) - self.assertEqual(res, COMPOSITE) - self.assertRaises(ValueError, lucas_test, -1) - - def test_is_prime(self): - primes = (170141183460469231731687303715884105727, - 19175002942688032928599, - 1363005552434666078217421284621279933627102780881053358473, - 2 ** 521 - 1) - for p in primes: - self.assertEqual(test_probable_prime(p), PROBABLY_PRIME) - - not_primes = ( - 4754868377601046732119933839981363081972014948522510826417784001, - 1334733877147062382486934807105197899496002201113849920496510541601, - 260849323075371835669784094383812120359260783810157225730623388382401, - ) - for np in not_primes: - self.assertEqual(test_probable_prime(np), COMPOSITE) - - from Cryptodome.Util.number import sieve_base - for p in sieve_base[:100]: - res = test_probable_prime(p) - self.assertEqual(res, PROBABLY_PRIME) - - def test_generate_prime_bit_size(self): - p = generate_probable_prime(exact_bits=512) - self.assertEqual(p.size_in_bits(), 512) - - def test_generate_prime_filter(self): - def ending_with_one(number): - return number % 10 == 1 - - for x in range(20): - q = generate_probable_prime(exact_bits=160, - prime_filter=ending_with_one) - self.assertEqual(q % 10, 1) - - def test_generate_safe_prime(self): - p = generate_probable_safe_prime(exact_bits=161) - self.assertEqual(p.size_in_bits(), 161) - -def get_tests(config={}): - tests = [] - tests += list_test_cases(TestPrimality) - return tests - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Math/test_modexp.py b/resources/lib/deps/Cryptodome/SelfTest/Math/test_modexp.py deleted file mode 100644 index d63f43c..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Math/test_modexp.py +++ /dev/null @@ -1,201 +0,0 @@ -# -# SelfTest/Math/test_modexp.py: Self-test for module exponentiation -# -# =================================================================== -# -# Copyright (c) 2017, Helder Eijs -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -"""Self-test for the custom module exponentiation""" - -import unittest - -from Cryptodome.SelfTest.st_common import list_test_cases - -from Cryptodome.Util.number import long_to_bytes, bytes_to_long - -from Cryptodome.Util.py3compat import * - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, - create_string_buffer, - get_raw_buffer, - c_size_t, - c_ulonglong) - -from Cryptodome.Hash import SHAKE128 -from Cryptodome.Math.Numbers import Integer -from Cryptodome.Math._IntegerCustom import _raw_montgomery - -from Cryptodome.Random.random import StrongRandom - - -def create_rng(tag): - rng = StrongRandom(SHAKE128.new(data=tag)) - return rng - -class ExceptionModulus(ValueError): - pass - -def monty_pow(base, exp, modulus): - max_len = len(long_to_bytes(max(base, exp, modulus))) - - base_b, exp_b, modulus_b = [ long_to_bytes(x, max_len) for x in - (base, exp, modulus) ] - - out = create_string_buffer(max_len) - error = _raw_montgomery.monty_pow( - out, - base_b, - exp_b, - modulus_b, - c_size_t(max_len), - c_ulonglong(32) - ) - - if error == 17: - raise ExceptionModulus() - if error: - raise ValueError("monty_pow failed with error: %d" % error) - - result = bytes_to_long(get_raw_buffer(out)) - return result - -exponent1 = 0x2ce0af628901460a419a08ef950d498b9fd6f271a1a52ac293b86fe5c60efe8e8ba93fa1ebe1eb3d614d2e7b328cb60a2591440e163441a190ecf101ceec245f600fffdcf3f5b3a17a7baeacb96a424db1d7ec985e8ec998bb479fecfffed6a75f9a90fc97062fd973303bce855ad7b8d8272a94025e8532be9aabd54a183f303538d2a7e621b4131d59e823a4625f39bd7d518d7784f7c3a8f19061da74974ff42fa1c063dec2db97d461e291a7d6e721708a5229de166c1246363372854e27f3f08ae274bc16bfd205b028a4d81386494433d516dfbb35f495acba5e4e1d1843cb3c3129b6642a85fc7244ce5845fac071c7f622e4ee12ac43fabeeaa0cd01 -modulus1 = 0xd66691b20071be4d66d4b71032b37fa007cfabf579fcb91e50bfc2753b3f0ce7be74e216aef7e26d4ae180bc20d7bd3ea88a6cbf6f87380e613c8979b5b043b200a8ff8856a3b12875e36e98a7569f3852d028e967551000b02c19e9fa52e83115b89309aabb1e1cf1e2cb6369d637d46775ce4523ea31f64ad2794cbc365dd8a35e007ed3b57695877fbf102dbeb8b3212491398e494314e93726926e1383f8abb5889bea954eb8c0ca1c62c8e9d83f41888095c5e645ed6d32515fe0c58c1368cad84694e18da43668c6f43e61d7c9bca633ddcda7aef5b79bc396d4a9f48e2a9abe0836cc455e435305357228e93d25aaed46b952defae0f57339bf26f5a9 - - -class TestModExp(unittest.TestCase): - - def test_small(self): - self.assertEqual(1, monty_pow(11,12,19)) - - def test_large_1(self): - base = 0xfffffffffffffffffffffffffffffffffffffffffffffffffff - expected = pow(base, exponent1, modulus1) - result = monty_pow(base, exponent1, modulus1) - self.assertEqual(result, expected) - - def test_zero_exp(self): - base = 0xfffffffffffffffffffffffffffffffffffffffffffffffffff - result = monty_pow(base, 0, modulus1) - self.assertEqual(result, 1) - - def test_zero_base(self): - result = monty_pow(0, exponent1, modulus1) - self.assertEqual(result, 0) - - def test_zero_modulus(self): - base = 0xfffffffffffffffffffffffffffffffffffffffffffffffff - self.assertRaises(ExceptionModulus, monty_pow, base, exponent1, 0) - self.assertRaises(ExceptionModulus, monty_pow, 0, 0, 0) - - def test_larger_exponent(self): - base = modulus1 - 0xFFFFFFF - expected = pow(base, modulus1<<64, modulus1) - result = monty_pow(base, modulus1<<64, modulus1) - self.assertEqual(result, expected) - - def test_even_modulus(self): - base = modulus1 >> 4 - self.assertRaises(ExceptionModulus, monty_pow, base, exponent1, modulus1-1) - - def test_several_lengths(self): - prng = SHAKE128.new().update(b('Test')) - for length in range(1, 100): - modulus2 = Integer.from_bytes(prng.read(length)) | 1 - base = Integer.from_bytes(prng.read(length)) % modulus2 - exponent2 = Integer.from_bytes(prng.read(length)) - - expected = pow(base, exponent2, modulus2) - result = monty_pow(base, exponent2, modulus2) - self.assertEqual(result, expected) - - def test_variable_exponent(self): - prng = create_rng(b('Test variable exponent')) - for i in range(20): - for j in range(7): - modulus = prng.getrandbits(8*30) | 1 - base = prng.getrandbits(8*30) % modulus - exponent = prng.getrandbits(i*8+j) - - expected = pow(base, exponent, modulus) - result = monty_pow(base, exponent, modulus) - self.assertEqual(result, expected) - - exponent ^= (1 << (i*8+j)) - 1 - - expected = pow(base, exponent, modulus) - result = monty_pow(base, exponent, modulus) - self.assertEqual(result, expected) - - def test_stress_63(self): - prng = create_rng(b('Test 63')) - length = 63 - for _ in range(2000): - modulus = prng.getrandbits(8*length) | 1 - base = prng.getrandbits(8*length) % modulus - exponent = prng.getrandbits(8*length) - - expected = pow(base, exponent, modulus) - result = monty_pow(base, exponent, modulus) - self.assertEqual(result, expected) - - def test_stress_64(self): - prng = create_rng(b('Test 64')) - length = 64 - for _ in range(2000): - modulus = prng.getrandbits(8*length) | 1 - base = prng.getrandbits(8*length) % modulus - exponent = prng.getrandbits(8*length) - - expected = pow(base, exponent, modulus) - result = monty_pow(base, exponent, modulus) - self.assertEqual(result, expected) - - def test_stress_65(self): - prng = create_rng(b('Test 65')) - length = 65 - for _ in range(2000): - modulus = prng.getrandbits(8*length) | 1 - base = prng.getrandbits(8*length) % modulus - exponent = prng.getrandbits(8*length) - - expected = pow(base, exponent, modulus) - result = monty_pow(base, exponent, modulus) - self.assertEqual(result, expected) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(TestModExp) - return tests - - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Math/test_modmult.py b/resources/lib/deps/Cryptodome/SelfTest/Math/test_modmult.py deleted file mode 100644 index df794e4..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Math/test_modmult.py +++ /dev/null @@ -1,120 +0,0 @@ -# -# SelfTest/Math/test_modmult.py: Self-test for custom modular multiplication -# -# =================================================================== -# -# Copyright (c) 2023, Helder Eijs -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -"""Self-test for the custom modular multiplication""" - -import unittest - -from Cryptodome.SelfTest.st_common import list_test_cases - -from Cryptodome.Util.number import long_to_bytes, bytes_to_long - -from Cryptodome.Util._raw_api import (create_string_buffer, - get_raw_buffer, - c_size_t) - -from Cryptodome.Math._IntegerCustom import _raw_montgomery - - -class ExceptionModulus(ValueError): - pass - - -def monty_mult(term1, term2, modulus): - - if term1 >= modulus: - term1 %= modulus - if term2 >= modulus: - term2 %= modulus - - modulus_b = long_to_bytes(modulus) - numbers_len = len(modulus_b) - term1_b = long_to_bytes(term1, numbers_len) - term2_b = long_to_bytes(term2, numbers_len) - - out = create_string_buffer(numbers_len) - error = _raw_montgomery.monty_multiply( - out, - term1_b, - term2_b, - modulus_b, - c_size_t(numbers_len) - ) - - if error == 17: - raise ExceptionModulus() - if error: - raise ValueError("monty_multiply() failed with error: %d" % error) - - return get_raw_buffer(out) - - -modulus1 = 0xd66691b20071be4d66d4b71032b37fa007cfabf579fcb91e50bfc2753b3f0ce7be74e216aef7e26d4ae180bc20d7bd3ea88a6cbf6f87380e613c8979b5b043b200a8ff8856a3b12875e36e98a7569f3852d028e967551000b02c19e9fa52e83115b89309aabb1e1cf1e2cb6369d637d46775ce4523ea31f64ad2794cbc365dd8a35e007ed3b57695877fbf102dbeb8b3212491398e494314e93726926e1383f8abb5889bea954eb8c0ca1c62c8e9d83f41888095c5e645ed6d32515fe0c58c1368cad84694e18da43668c6f43e61d7c9bca633ddcda7aef5b79bc396d4a9f48e2a9abe0836cc455e435305357228e93d25aaed46b952defae0f57339bf26f5a9 - - -class TestModMultiply(unittest.TestCase): - - def test_small(self): - self.assertEqual(b"\x01", monty_mult(5, 6, 29)) - - def test_large(self): - numbers_len = (modulus1.bit_length() + 7) // 8 - - t1 = modulus1 // 2 - t2 = modulus1 - 90 - expect = b'\x00' * (numbers_len - 1) + b'\x2d' - self.assertEqual(expect, monty_mult(t1, t2, modulus1)) - - def test_zero_term(self): - numbers_len = (modulus1.bit_length() + 7) // 8 - expect = b'\x00' * numbers_len - self.assertEqual(expect, monty_mult(0x100, 0, modulus1)) - self.assertEqual(expect, monty_mult(0, 0x100, modulus1)) - - def test_larger_term(self): - t1 = 2**2047 - expect_int = 0x8edf4071f78e3d7ba622cdbbbef74612e301d69186776ae6bf87ff38c320d9aebaa64889c2f67de2324e6bccd2b10ad89e91fd21ba4bb523904d033eff5e70e62f01a84f41fa90a4f248ef249b82e1d2729253fdfc2a3b5b740198123df8bfbf7057d03e15244ad5f26eb9a099763b5c5972121ec076b0bf899f59bd95f7cc129abddccf24217bce52ca0f3a44c9ccc504765dbb89734205f3ae6a8cc560494a60ea84b27d8e00fa24bdd5b4f1d4232edb61e47d3d984c1fa50a3820a2e580fbc3fc8bc11e99df53b9efadf5a40ac75d384e400905aa6f1d88950cd53b1c54dc2222115ad84a27260fa4d978155c1434c551de1ee7361a17a2f79d4388f78a5d - res = bytes_to_long(monty_mult(t1, t1, modulus1)) - self.assertEqual(res, expect_int) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(TestModMultiply) - return tests - - -if __name__ == '__main__': - def suite(): - return unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Protocol/__init__.py b/resources/lib/deps/Cryptodome/SelfTest/Protocol/__init__.py deleted file mode 100644 index a349e84..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Protocol/__init__.py +++ /dev/null @@ -1,45 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Protocol/__init__.py: Self-tests for Cryptodome.Protocol -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test for Cryptodome.Protocol""" - -__revision__ = "$Id$" - -def get_tests(config={}): - tests = [] - from Cryptodome.SelfTest.Protocol import test_rfc1751; tests += test_rfc1751.get_tests(config=config) - from Cryptodome.SelfTest.Protocol import test_KDF; tests += test_KDF.get_tests(config=config) - from Cryptodome.SelfTest.Protocol import test_ecdh; tests += test_ecdh.get_tests(config=config) - - from Cryptodome.SelfTest.Protocol import test_SecretSharing; - tests += test_SecretSharing.get_tests(config=config) - - return tests - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Protocol/test_KDF.py b/resources/lib/deps/Cryptodome/SelfTest/Protocol/test_KDF.py deleted file mode 100644 index f2c5b11..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Protocol/test_KDF.py +++ /dev/null @@ -1,807 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Protocol/test_KDF.py: Self-test for key derivation functions -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -import re -import unittest -from binascii import unhexlify - -from Cryptodome.Util.py3compat import b, bchr - -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.SelfTest.loader import load_test_vectors, load_test_vectors_wycheproof -from Cryptodome.Hash import SHA1, HMAC, SHA256, MD5, SHA224, SHA384, SHA512 -from Cryptodome.Cipher import AES, DES3 - -from Cryptodome.Protocol.KDF import (PBKDF1, PBKDF2, _S2V, HKDF, scrypt, - bcrypt, bcrypt_check, - SP800_108_Counter) - -from Cryptodome.Protocol.KDF import _bcrypt_decode - - -def t2b(t): - if t is None: - return None - t2 = t.replace(" ", "").replace("\n", "") - return unhexlify(b(t2)) - - -class TestVector(object): - pass - - -class PBKDF1_Tests(unittest.TestCase): - - # List of tuples with test data. - # Each tuple is made up by: - # Item #0: a pass phrase - # Item #1: salt (8 bytes encoded in hex) - # Item #2: output key length - # Item #3: iterations to use - # Item #4: expected result (encoded in hex) - _testData = ( - # From http://www.di-mgt.com.au/cryptoKDFs.html#examplespbkdf - ("password", "78578E5A5D63CB06", 16, 1000, "DC19847E05C64D2FAF10EBFB4A3D2A20"), - ) - - def test1(self): - v = self._testData[0] - res = PBKDF1(v[0], t2b(v[1]), v[2], v[3], SHA1) - self.assertEqual(res, t2b(v[4])) - - -class PBKDF2_Tests(unittest.TestCase): - - # List of tuples with test data. - # Each tuple is made up by: - # Item #0: a pass phrase - # Item #1: salt (encoded in hex) - # Item #2: output key length - # Item #3: iterations to use - # Item #4: hash module - # Item #5: expected result (encoded in hex) - _testData = ( - # From http://www.di-mgt.com.au/cryptoKDFs.html#examplespbkdf - ("password","78578E5A5D63CB06",24,2048, SHA1, "BFDE6BE94DF7E11DD409BCE20A0255EC327CB936FFE93643"), - # From RFC 6050 - ("password","73616c74", 20, 1, SHA1, "0c60c80f961f0e71f3a9b524af6012062fe037a6"), - ("password","73616c74", 20, 2, SHA1, "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"), - ("password","73616c74", 20, 4096, SHA1, "4b007901b765489abead49d926f721d065a429c1"), - ("passwordPASSWORDpassword","73616c7453414c5473616c7453414c5473616c7453414c5473616c7453414c5473616c74", - 25, 4096, SHA1, "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"), - ( 'pass\x00word',"7361006c74",16,4096, SHA1, "56fa6aa75548099dcc37d7f03425e0c3"), - # From draft-josefsson-scrypt-kdf-01, Chapter 10 - ( 'passwd', '73616c74', 64, 1, SHA256, "55ac046e56e3089fec1691c22544b605f94185216dde0465e68b9d57c20dacbc49ca9cccf179b645991664b39d77ef317c71b845b1e30bd509112041d3a19783"), - ( 'Password', '4e61436c', 64, 80000, SHA256, "4ddcd8f60b98be21830cee5ef22701f9641a4418d04c0414aeff08876b34ab56a1d425a1225833549adb841b51c9b3176a272bdebba1d078478f62b397f33c8d"), - ) - - def test1(self): - # Test only for HMAC-SHA1 as PRF - - def prf_SHA1(p,s): - return HMAC.new(p,s,SHA1).digest() - - def prf_SHA256(p,s): - return HMAC.new(p,s,SHA256).digest() - - for i in range(len(self._testData)): - v = self._testData[i] - password = v[0] - salt = t2b(v[1]) - out_len = v[2] - iters = v[3] - hash_mod = v[4] - expected = t2b(v[5]) - - if hash_mod is SHA1: - res = PBKDF2(password, salt, out_len, iters) - self.assertEqual(res, expected) - - res = PBKDF2(password, salt, out_len, iters, prf_SHA1) - self.assertEqual(res, expected) - else: - res = PBKDF2(password, salt, out_len, iters, prf_SHA256) - self.assertEqual(res, expected) - - def test2(self): - # Verify that prf and hmac_hash_module are mutual exclusive - def prf_SHA1(p,s): - return HMAC.new(p,s,SHA1).digest() - - self.assertRaises(ValueError, PBKDF2, b("xxx"), b("yyy"), 16, 100, - prf=prf_SHA1, hmac_hash_module=SHA1) - - def test3(self): - # Verify that hmac_hash_module works like prf - - password = b("xxx") - salt = b("yyy") - - for hashmod in (MD5, SHA1, SHA224, SHA256, SHA384, SHA512): - - pr1 = PBKDF2(password, salt, 16, 100, - prf=lambda p, s: HMAC.new(p,s,hashmod).digest()) - pr2 = PBKDF2(password, salt, 16, 100, hmac_hash_module=hashmod) - - self.assertEqual(pr1, pr2) - - def test4(self): - # Verify that PBKDF2 can take bytes or strings as password or salt - k1 = PBKDF2("xxx", b("yyy"), 16, 10) - k2 = PBKDF2(b("xxx"), b("yyy"), 16, 10) - self.assertEqual(k1, k2) - - k1 = PBKDF2(b("xxx"), "yyy", 16, 10) - k2 = PBKDF2(b("xxx"), b("yyy"), 16, 10) - self.assertEqual(k1, k2) - - -class S2V_Tests(unittest.TestCase): - - # Sequence of test vectors. - # Each test vector is made up by: - # Item #0: a tuple of strings - # Item #1: an AES key - # Item #2: the result - # Item #3: the cipher module S2V is based on - # Everything is hex encoded - _testData = [ - - # RFC5297, A.1 - ( - ( '101112131415161718191a1b1c1d1e1f2021222324252627', - '112233445566778899aabbccddee' ), - 'fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0', - '85632d07c6e8f37f950acd320a2ecc93', - AES - ), - - # RFC5297, A.2 - ( - ( '00112233445566778899aabbccddeeffdeaddadadeaddadaffeeddcc'+ - 'bbaa99887766554433221100', - '102030405060708090a0', - '09f911029d74e35bd84156c5635688c0', - '7468697320697320736f6d6520706c61'+ - '696e7465787420746f20656e63727970'+ - '74207573696e67205349562d414553'), - '7f7e7d7c7b7a79787776757473727170', - '7bdb6e3b432667eb06f4d14bff2fbd0f', - AES - ), - - ] - - def test1(self): - """Verify correctness of test vector""" - for tv in self._testData: - s2v = _S2V.new(t2b(tv[1]), tv[3]) - for s in tv[0]: - s2v.update(t2b(s)) - result = s2v.derive() - self.assertEqual(result, t2b(tv[2])) - - def test2(self): - """Verify that no more than 127(AES) and 63(TDES) - components are accepted.""" - key = bchr(0) * 8 + bchr(255) * 8 - for module in (AES, DES3): - s2v = _S2V.new(key, module) - max_comps = module.block_size*8-1 - for i in range(max_comps): - s2v.update(b("XX")) - self.assertRaises(TypeError, s2v.update, b("YY")) - - -class HKDF_Tests(unittest.TestCase): - - # Test vectors from RFC5869, Appendix A - # Each tuple is made up by: - # Item #0: hash module - # Item #1: secret - # Item #2: salt - # Item #3: context - # Item #4: expected result - _test_vector = ( - ( - SHA256, - "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", - "000102030405060708090a0b0c", - "f0f1f2f3f4f5f6f7f8f9", - 42, - "3cb25f25faacd57a90434f64d0362f2a" + - "2d2d0a90cf1a5a4c5db02d56ecc4c5bf" + - "34007208d5b887185865" - ), - ( - SHA256, - "000102030405060708090a0b0c0d0e0f" + - "101112131415161718191a1b1c1d1e1f" + - "202122232425262728292a2b2c2d2e2f" + - "303132333435363738393a3b3c3d3e3f" + - "404142434445464748494a4b4c4d4e4f", - "606162636465666768696a6b6c6d6e6f" + - "707172737475767778797a7b7c7d7e7f" + - "808182838485868788898a8b8c8d8e8f" + - "909192939495969798999a9b9c9d9e9f" + - "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf", - "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + - "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + - "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + - "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + - "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", - 82, - "b11e398dc80327a1c8e7f78c596a4934" + - "4f012eda2d4efad8a050cc4c19afa97c" + - "59045a99cac7827271cb41c65e590e09" + - "da3275600c2f09b8367793a9aca3db71" + - "cc30c58179ec3e87c14c01d5c1f3434f" + - "1d87" - ), - ( - SHA256, - "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", - None, - None, - 42, - "8da4e775a563c18f715f802a063c5a31" + - "b8a11f5c5ee1879ec3454e5f3c738d2d" + - "9d201395faa4b61a96c8" - ), - ( - SHA1, - "0b0b0b0b0b0b0b0b0b0b0b", - "000102030405060708090a0b0c", - "f0f1f2f3f4f5f6f7f8f9", - 42, - "085a01ea1b10f36933068b56efa5ad81" + - "a4f14b822f5b091568a9cdd4f155fda2" + - "c22e422478d305f3f896" - ), - ( - SHA1, - "000102030405060708090a0b0c0d0e0f" + - "101112131415161718191a1b1c1d1e1f" + - "202122232425262728292a2b2c2d2e2f" + - "303132333435363738393a3b3c3d3e3f" + - "404142434445464748494a4b4c4d4e4f", - "606162636465666768696a6b6c6d6e6f" + - "707172737475767778797a7b7c7d7e7f" + - "808182838485868788898a8b8c8d8e8f" + - "909192939495969798999a9b9c9d9e9f" + - "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf", - "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + - "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + - "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + - "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + - "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", - 82, - "0bd770a74d1160f7c9f12cd5912a06eb" + - "ff6adcae899d92191fe4305673ba2ffe" + - "8fa3f1a4e5ad79f3f334b3b202b2173c" + - "486ea37ce3d397ed034c7f9dfeb15c5e" + - "927336d0441f4c4300e2cff0d0900b52" + - "d3b4" - ), - ( - SHA1, - "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", - "", - "", - 42, - "0ac1af7002b3d761d1e55298da9d0506" + - "b9ae52057220a306e07b6b87e8df21d0" + - "ea00033de03984d34918" - ), - ( - SHA1, - "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", - None, - "", - 42, - "2c91117204d745f3500d636a62f64f0a" + - "b3bae548aa53d423b0d1f27ebba6f5e5" + - "673a081d70cce7acfc48" - ) - ) - - def test1(self): - for tv in self._test_vector: - secret, salt, info, exp = [ t2b(tv[x]) for x in (1,2,3,5) ] - key_len, hashmod = [ tv[x] for x in (4,0) ] - - output = HKDF(secret, key_len, salt, hashmod, 1, info) - self.assertEqual(output, exp) - - def test2(self): - ref = HKDF(b("XXXXXX"), 12, b("YYYY"), SHA1) - - # Same output, but this time split over 2 keys - key1, key2 = HKDF(b("XXXXXX"), 6, b("YYYY"), SHA1, 2) - self.assertEqual((ref[:6], ref[6:]), (key1, key2)) - - # Same output, but this time split over 3 keys - key1, key2, key3 = HKDF(b("XXXXXX"), 4, b("YYYY"), SHA1, 3) - self.assertEqual((ref[:4], ref[4:8], ref[8:]), (key1, key2, key3)) - - -class scrypt_Tests(unittest.TestCase): - - # Test vectors taken from - # https://tools.ietf.org/html/rfc7914 - # - password - # - salt - # - N - # - r - # - p - data = ( - ( - "", - "", - 16, # 2K - 1, - 1, - """ - 77 d6 57 62 38 65 7b 20 3b 19 ca 42 c1 8a 04 97 - f1 6b 48 44 e3 07 4a e8 df df fa 3f ed e2 14 42 - fc d0 06 9d ed 09 48 f8 32 6a 75 3a 0f c8 1f 17 - e8 d3 e0 fb 2e 0d 36 28 cf 35 e2 0c 38 d1 89 06 - """ - ), - ( - "password", - "NaCl", - 1024, # 1M - 8, - 16, - """ - fd ba be 1c 9d 34 72 00 78 56 e7 19 0d 01 e9 fe - 7c 6a d7 cb c8 23 78 30 e7 73 76 63 4b 37 31 62 - 2e af 30 d9 2e 22 a3 88 6f f1 09 27 9d 98 30 da - c7 27 af b9 4a 83 ee 6d 83 60 cb df a2 cc 06 40 - """ - ), - ( - "pleaseletmein", - "SodiumChloride", - 16384, # 16M - 8, - 1, - """ - 70 23 bd cb 3a fd 73 48 46 1c 06 cd 81 fd 38 eb - fd a8 fb ba 90 4f 8e 3e a9 b5 43 f6 54 5d a1 f2 - d5 43 29 55 61 3f 0f cf 62 d4 97 05 24 2a 9a f9 - e6 1e 85 dc 0d 65 1e 40 df cf 01 7b 45 57 58 87 - """ - ), - ( - "pleaseletmein", - "SodiumChloride", - 1048576, # 1G - 8, - 1, - """ - 21 01 cb 9b 6a 51 1a ae ad db be 09 cf 70 f8 81 - ec 56 8d 57 4a 2f fd 4d ab e5 ee 98 20 ad aa 47 - 8e 56 fd 8f 4b a5 d0 9f fa 1c 6d 92 7c 40 f4 c3 - 37 30 40 49 e8 a9 52 fb cb f4 5c 6f a7 7a 41 a4 - """ - ), - ) - - def setUp(self): - new_test_vectors = [] - for tv in self.data: - new_tv = TestVector() - new_tv.P = b(tv[0]) - new_tv.S = b(tv[1]) - new_tv.N = tv[2] - new_tv.r = tv[3] - new_tv.p = tv[4] - new_tv.output = t2b(tv[5]) - new_tv.dkLen = len(new_tv.output) - new_test_vectors.append(new_tv) - self.data = new_test_vectors - - def test2(self): - - for tv in self.data: - try: - output = scrypt(tv.P, tv.S, tv.dkLen, tv.N, tv.r, tv.p) - except ValueError as e: - if " 2 " in str(e) and tv.N >= 1048576: - import warnings - warnings.warn("Not enough memory to unit test scrypt() with N=1048576", RuntimeWarning) - continue - else: - raise e - self.assertEqual(output, tv.output) - - def test3(self): - ref = scrypt(b("password"), b("salt"), 12, 16, 1, 1) - - # Same output, but this time split over 2 keys - key1, key2 = scrypt(b("password"), b("salt"), 6, 16, 1, 1, 2) - self.assertEqual((ref[:6], ref[6:]), (key1, key2)) - - # Same output, but this time split over 3 keys - key1, key2, key3 = scrypt(b("password"), b("salt"), 4, 16, 1, 1, 3) - self.assertEqual((ref[:4], ref[4:8], ref[8:]), (key1, key2, key3)) - - -class bcrypt_Tests(unittest.TestCase): - - def test_negative_cases(self): - self.assertRaises(ValueError, bcrypt, b"1" * 73, 10) - self.assertRaises(ValueError, bcrypt, b"1" * 10, 3) - self.assertRaises(ValueError, bcrypt, b"1" * 10, 32) - self.assertRaises(ValueError, bcrypt, b"1" * 10, 4, salt=b"") - self.assertRaises(ValueError, bcrypt, b"1" * 10, 4, salt=b"1") - self.assertRaises(ValueError, bcrypt, b"1" * 10, 4, salt=b"1" * 17) - self.assertRaises(ValueError, bcrypt, b"1\x00" * 10, 4) - - def test_bytearray_mismatch(self): - ref = bcrypt("pwd", 4) - bcrypt_check("pwd", ref) - bref = bytearray(ref) - bcrypt_check("pwd", bref) - - wrong = ref[:-1] + bchr(bref[-1] ^ 0x01) - self.assertRaises(ValueError, bcrypt_check, "pwd", wrong) - - wrong = b"x" + ref[1:] - self.assertRaises(ValueError, bcrypt_check, "pwd", wrong) - - # https://github.com/patrickfav/bcrypt/wiki/Published-Test-Vectors - - def test_empty_password(self): - # password, cost, salt, bcrypt hash - tvs = [ - (b"", 4, b"zVHmKQtGGQob.b/Nc7l9NO", b"$2a$04$zVHmKQtGGQob.b/Nc7l9NO8UlrYcW05FiuCj/SxsFO/ZtiN9.mNzy"), - (b"", 5, b"zVHmKQtGGQob.b/Nc7l9NO", b"$2a$05$zVHmKQtGGQob.b/Nc7l9NOWES.1hkVBgy5IWImh9DOjKNU8atY4Iy"), - (b"", 6, b"zVHmKQtGGQob.b/Nc7l9NO", b"$2a$06$zVHmKQtGGQob.b/Nc7l9NOjOl7l4oz3WSh5fJ6414Uw8IXRAUoiaO"), - (b"", 7, b"zVHmKQtGGQob.b/Nc7l9NO", b"$2a$07$zVHmKQtGGQob.b/Nc7l9NOBsj1dQpBA1HYNGpIETIByoNX9jc.hOi"), - (b"", 8, b"zVHmKQtGGQob.b/Nc7l9NO", b"$2a$08$zVHmKQtGGQob.b/Nc7l9NOiLTUh/9MDpX86/DLyEzyiFjqjBFePgO"), - ] - - for (idx, (password, cost, salt64, result)) in enumerate(tvs): - x = bcrypt(password, cost, salt=_bcrypt_decode(salt64)) - self.assertEqual(x, result) - bcrypt_check(password, result) - - def test_random_password_and_salt_short_pw(self): - # password, cost, salt, bcrypt hash - tvs = [ - (b"<.S.2K(Zq'", 4, b"VYAclAMpaXY/oqAo9yUpku", b"$2a$04$VYAclAMpaXY/oqAo9yUpkuWmoYywaPzyhu56HxXpVltnBIfmO9tgu"), - (b"5.rApO%5jA", 5, b"kVNDrnYKvbNr5AIcxNzeIu", b"$2a$05$kVNDrnYKvbNr5AIcxNzeIuRcyIF5cZk6UrwHGxENbxP5dVv.WQM/G"), - (b"oW++kSrQW^", 6, b"QLKkRMH9Am6irtPeSKN5sO", b"$2a$06$QLKkRMH9Am6irtPeSKN5sObJGr3j47cO6Pdf5JZ0AsJXuze0IbsNm"), - (b"ggJ\\KbTnDG", 7, b"4H896R09bzjhapgCPS/LYu", b"$2a$07$4H896R09bzjhapgCPS/LYuMzAQluVgR5iu/ALF8L8Aln6lzzYXwbq"), - (b"49b0:;VkH/", 8, b"hfvO2retKrSrx5f2RXikWe", b"$2a$08$hfvO2retKrSrx5f2RXikWeFWdtSesPlbj08t/uXxCeZoHRWDz/xFe"), - (b">9N^5jc##'", 9, b"XZLvl7rMB3EvM0c1.JHivu", b"$2a$09$XZLvl7rMB3EvM0c1.JHivuIDPJWeNJPTVrpjZIEVRYYB/mF6cYgJK"), - (b"\\$ch)s4WXp", 10, b"aIjpMOLK5qiS9zjhcHR5TO", b"$2a$10$aIjpMOLK5qiS9zjhcHR5TOU7v2NFDmcsBmSFDt5EHOgp/jeTF3O/q"), - (b"RYoj\\_>2P7", 12, b"esIAHiQAJNNBrsr5V13l7.", b"$2a$12$esIAHiQAJNNBrsr5V13l7.RFWWJI2BZFtQlkFyiWXjou05GyuREZa"), - ] - - for (idx, (password, cost, salt64, result)) in enumerate(tvs): - x = bcrypt(password, cost, salt=_bcrypt_decode(salt64)) - self.assertEqual(x, result) - bcrypt_check(password, result) - - def test_random_password_and_salt_long_pw(self): - # password, cost, salt, bcrypt hash - tvs = [ - (b"^Q&\"]A`%/A(BVGt>QaX0M-#1ghq_+\":Y0CRmY", 5, b"YuQvhokOGVnevctykUYpKu", b"$2a$05$YuQvhokOGVnevctykUYpKutZD2pWeGGYn3auyLOasguMY3/0BbIyq"), - (b"F%uN/j>[GuB7-jB'_Yj!Tnb7Y!u^6)", 6, b"5L3vpQ0tG9O7k5gQ8nAHAe", b"$2a$06$5L3vpQ0tG9O7k5gQ8nAHAe9xxQiOcOLh8LGcI0PLWhIznsDt.S.C6"), - (b"Z>BobP32ub\"Cfe*Q<-q-=tRSjOBh8\\mLNW.", 9, b"nArqOfdCsD9kIbVnAixnwe", b"$2a$09$nArqOfdCsD9kIbVnAixnwe6s8QvyPYWtQBpEXKir2OJF9/oNBsEFe"), - (b"/MH51`!BP&0tj3%YCA;Xk%e3S`o\\EI", 10, b"ePiAc.s.yoBi3B6p1iQUCe", b"$2a$10$ePiAc.s.yoBi3B6p1iQUCezn3mraLwpVJ5XGelVyYFKyp5FZn/y.u"), - (b"ptAP\"mcg6oH.\";c0U2_oll.OKi5?Ui\"^ai#iQH7ZFtNMfs3AROnIncE9\"BNNoEgO[[*Yk8;RQ(#S,;I+aT", - 5, b"wgkOlGNXIVE2fWkT3gyRoO", b"$2a$05$wgkOlGNXIVE2fWkT3gyRoOqWi4gbi1Wv2Q2Jx3xVs3apl1w.Wtj8C"), - (b"M.E1=dt<.L0Q&p;94NfGm_Oo23+Kpl@M5?WIAL.[@/:'S)W96G8N^AWb7_smmC]>7#fGoB", - 6, b"W9zTCl35nEvUukhhFzkKMe", b"$2a$06$W9zTCl35nEvUukhhFzkKMekjT9/pj7M0lihRVEZrX3m8/SBNZRX7i"), - ] - - for (idx, (password, cost, salt64, result)) in enumerate(tvs): - x = bcrypt(password, cost, salt=_bcrypt_decode(salt64)) - self.assertEqual(x, result) - bcrypt_check(password, result) - - def test_increasing_password_length(self): - # password, cost, salt, bcrypt hash - tvs = [ - (b"a", 4, b"5DCebwootqWMCp59ISrMJ.", b"$2a$04$5DCebwootqWMCp59ISrMJ.l4WvgHIVg17ZawDIrDM2IjlE64GDNQS"), - (b"aa", 4, b"5DCebwootqWMCp59ISrMJ.", b"$2a$04$5DCebwootqWMCp59ISrMJ.AyUxBk.ThHlsLvRTH7IqcG7yVHJ3SXq"), - (b"aaa", 4, b"5DCebwootqWMCp59ISrMJ.", b"$2a$04$5DCebwootqWMCp59ISrMJ.BxOVac5xPB6XFdRc/ZrzM9FgZkqmvbW"), - (b"aaaa", 4, b"5DCebwootqWMCp59ISrMJ.", b"$2a$04$5DCebwootqWMCp59ISrMJ.Qbr209bpCtfl5hN7UQlG/L4xiD3AKau"), - (b"aaaaa", 4, b"5DCebwootqWMCp59ISrMJ.", b"$2a$04$5DCebwootqWMCp59ISrMJ.oWszihPjDZI0ypReKsaDOW1jBl7oOii"), - (b"aaaaaa", 4, b"5DCebwootqWMCp59ISrMJ.", b"$2a$04$5DCebwootqWMCp59ISrMJ./k.Xxn9YiqtV/sxh3EHbnOHd0Qsq27K"), - (b"aaaaaaa", 4, b"5DCebwootqWMCp59ISrMJ.", b"$2a$04$5DCebwootqWMCp59ISrMJ.PYJqRFQbgRbIjMd5VNKmdKS4sBVOyDe"), - (b"aaaaaaaa", 4, b"5DCebwootqWMCp59ISrMJ.", b"$2a$04$5DCebwootqWMCp59ISrMJ..VMYfzaw1wP/SGxowpLeGf13fxCCt.q"), - (b"aaaaaaaaa", 4, b"5DCebwootqWMCp59ISrMJ.", b"$2a$04$5DCebwootqWMCp59ISrMJ.5B0p054nO5WgAD1n04XslDY/bqY9RJi"), - (b"aaaaaaaaaa", 4, b"5DCebwootqWMCp59ISrMJ.", b"$2a$04$5DCebwootqWMCp59ISrMJ.INBTgqm7sdlBJDg.J5mLMSRK25ri04y"), - (b"aaaaaaaaaaa", 4, b"5DCebwootqWMCp59ISrMJ.", b"$2a$04$5DCebwootqWMCp59ISrMJ.s3y7CdFD0OR5p6rsZw/eZ.Dla40KLfm"), - (b"aaaaaaaaaaaa", 4, b"5DCebwootqWMCp59ISrMJ.", b"$2a$04$5DCebwootqWMCp59ISrMJ.Jx742Djra6Q7PqJWnTAS.85c28g.Siq"), - (b"aaaaaaaaaaaaa", 4, b"5DCebwootqWMCp59ISrMJ.", b"$2a$04$5DCebwootqWMCp59ISrMJ.oKMXW3EZcPHcUV0ib5vDBnh9HojXnLu"), - (b"aaaaaaaaaaaaaa", 4, b"5DCebwootqWMCp59ISrMJ.", b"$2a$04$5DCebwootqWMCp59ISrMJ.w6nIjWpDPNSH5pZUvLjC1q25ONEQpeS"), - (b"aaaaaaaaaaaaaaa", 4, b"5DCebwootqWMCp59ISrMJ.", b"$2a$04$5DCebwootqWMCp59ISrMJ.k1b2/r9A/hxdwKEKurg6OCn4MwMdiGq"), - (b"aaaaaaaaaaaaaaaa", 4, b"5DCebwootqWMCp59ISrMJ.", b"$2a$04$5DCebwootqWMCp59ISrMJ.3prCNHVX1Ws.7Hm2bJxFUnQOX9f7DFa"), - ] - - for (idx, (password, cost, salt64, result)) in enumerate(tvs): - x = bcrypt(password, cost, salt=_bcrypt_decode(salt64)) - self.assertEqual(x, result) - bcrypt_check(password, result) - - def test_non_ascii_characters(self): - # password, cost, salt, bcrypt hash - tvs = [ - ("àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝðÐ", 4, b"D3qS2aoTVyqM7z8v8crLm.", b"$2a$04$D3qS2aoTVyqM7z8v8crLm.3nKt4CzBZJbyFB.ZebmfCvRw7BGs.Xm"), - ("àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝðÐ", 5, b"VA1FujiOCMPkUHQ8kF7IaO", b"$2a$05$VA1FujiOCMPkUHQ8kF7IaOg7NGaNvpxwWzSluQutxEVmbZItRTsAa"), - ("àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝðÐ", 6, b"TXiaNrPeBSz5ugiQlehRt.", b"$2a$06$TXiaNrPeBSz5ugiQlehRt.gwpeDQnXWteQL4z2FulouBr6G7D9KUi"), - ("âêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿ", 4, b"YTn1Qlvps8e1odqMn6G5x.", b"$2a$04$YTn1Qlvps8e1odqMn6G5x.85pqKql6w773EZJAExk7/BatYAI4tyO"), - ("âêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿ", 5, b"C.8k5vJKD2NtfrRI9o17DO", b"$2a$05$C.8k5vJKD2NtfrRI9o17DOfIW0XnwItA529vJnh2jzYTb1QdoY0py"), - ("âêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿ", 6, b"xqfRPj3RYAgwurrhcA6uRO", b"$2a$06$xqfRPj3RYAgwurrhcA6uROtGlXDp/U6/gkoDYHwlubtcVcNft5.vW"), - ("ÄËÏÖÜŸåÅæÆœŒßçÇøØ¢¿¡€", 4, b"y8vGgMmr9EdyxP9rmMKjH.", b"$2a$04$y8vGgMmr9EdyxP9rmMKjH.wv2y3r7yRD79gykQtmb3N3zrwjKsyay"), - ("ÄËÏÖÜŸåÅæÆœŒßçÇøØ¢¿¡€", 5, b"iYH4XIKAOOm/xPQs7xKP1u", b"$2a$05$iYH4XIKAOOm/xPQs7xKP1upD0cWyMn3Jf0ZWiizXbEkVpS41K1dcO"), - ("ÄËÏÖÜŸåÅæÆœŒßçÇøØ¢¿¡€", 6, b"wCOob.D0VV8twafNDB2ape", b"$2a$06$wCOob.D0VV8twafNDB2apegiGD5nqF6Y1e6K95q6Y.R8C4QGd265q"), - ("ΔημοσιεύθηκεστηνΕφημερίδατης", 4, b"E5SQtS6P4568MDXW7cyUp.", b"$2a$04$E5SQtS6P4568MDXW7cyUp.18wfDisKZBxifnPZjAI1d/KTYMfHPYO"), - ("АБбВвГгДдЕеЁёЖжЗзИиЙйКкЛлМмН", 4, b"03e26gQFHhQwRNf81/ww9.", b"$2a$04$03e26gQFHhQwRNf81/ww9.p1UbrNwxpzWjLuT.zpTLH4t/w5WhAhC"), - ("нОоПпРрСсТтУуФфХхЦцЧчШшЩщЪъЫыЬьЭэЮю", 4, b"PHNoJwpXCfe32nUtLv2Upu", b"$2a$04$PHNoJwpXCfe32nUtLv2UpuhJXOzd4k7IdFwnEpYwfJVCZ/f/.8Pje"), - ("電电電島岛島兔兔兎龜龟亀國国国區区区", 4, b"wU4/0i1TmNl2u.1jIwBX.u", b"$2a$04$wU4/0i1TmNl2u.1jIwBX.uZUaOL3Rc5ID7nlQRloQh6q5wwhV/zLW"), - ("诶比伊艾弗豆贝尔维吾艾尺开艾丝维贼德", 4, b"P4kreGLhCd26d4WIy7DJXu", b"$2a$04$P4kreGLhCd26d4WIy7DJXusPkhxLvBouzV6OXkL5EB0jux0osjsry"), - ] - - for (idx, (password, cost, salt64, result)) in enumerate(tvs): - x = bcrypt(password, cost, salt=_bcrypt_decode(salt64)) - self.assertEqual(x, result) - bcrypt_check(password, result) - - def test_special_case_salt(self): - # password, cost, salt, bcrypt hash - tvs = [ - ("-O_=*N!2JP", 4, b"......................", b"$2a$04$......................JjuKLOX9OOwo5PceZZXSkaLDvdmgb82"), - ("7B[$Q<4b>U", 5, b"......................", b"$2a$05$......................DRiedDQZRL3xq5A5FL8y7/6NM8a2Y5W"), - (">d5-I_8^.h", 6, b"......................", b"$2a$06$......................5Mq1Ng8jgDY.uHNU4h5p/x6BedzNH2W"), - (")V`/UM/]1t", 4, b".OC/.OC/.OC/.OC/.OC/.O", b"$2a$04$.OC/.OC/.OC/.OC/.OC/.OQIvKRDAam.Hm5/IaV/.hc7P8gwwIbmi"), - (":@t2.bWuH]", 5, b".OC/.OC/.OC/.OC/.OC/.O", b"$2a$05$.OC/.OC/.OC/.OC/.OC/.ONDbUvdOchUiKmQORX6BlkPofa/QxW9e"), - ("b(#KljF5s\"", 6, b".OC/.OC/.OC/.OC/.OC/.O", b"$2a$06$.OC/.OC/.OC/.OC/.OC/.OHfTd9e7svOu34vi1PCvOcAEq07ST7.K"), - ("@3YaJ^Xs]*", 4, b"eGA.eGA.eGA.eGA.eGA.e.", b"$2a$04$eGA.eGA.eGA.eGA.eGA.e.stcmvh.R70m.0jbfSFVxlONdj1iws0C"), - ("'\"5\\!k*C(p", 5, b"eGA.eGA.eGA.eGA.eGA.e.", b"$2a$05$eGA.eGA.eGA.eGA.eGA.e.vR37mVSbfdHwu.F0sNMvgn8oruQRghy"), - ("edEu7C?$'W", 6, b"eGA.eGA.eGA.eGA.eGA.e.", b"$2a$06$eGA.eGA.eGA.eGA.eGA.e.tSq0FN8MWHQXJXNFnHTPQKtA.n2a..G"), - ("N7dHmg\\PI^", 4, b"999999999999999999999u", b"$2a$04$999999999999999999999uCZfA/pLrlyngNDMq89r1uUk.bQ9icOu"), - ("\"eJuHh!)7*", 5, b"999999999999999999999u", b"$2a$05$999999999999999999999uj8Pfx.ufrJFAoWFLjapYBS5vVEQQ/hK"), - ("ZeDRJ:_tu:", 6, b"999999999999999999999u", b"$2a$06$999999999999999999999u6RB0P9UmbdbQgjoQFEJsrvrKe.BoU6q"), - ] - - for (idx, (password, cost, salt64, result)) in enumerate(tvs): - x = bcrypt(password, cost, salt=_bcrypt_decode(salt64)) - self.assertEqual(x, result) - bcrypt_check(password, result) - - -class TestVectorsHKDFWycheproof(unittest.TestCase): - - def __init__(self, wycheproof_warnings): - unittest.TestCase.__init__(self) - self._wycheproof_warnings = wycheproof_warnings - self._id = "None" - - def add_tests(self, filename): - - def filter_algo(root): - algo_name = root['algorithm'] - if algo_name == "HKDF-SHA-1": - return SHA1 - elif algo_name == "HKDF-SHA-256": - return SHA256 - elif algo_name == "HKDF-SHA-384": - return SHA384 - elif algo_name == "HKDF-SHA-512": - return SHA512 - else: - raise ValueError("Unknown algorithm " + algo_name) - - def filter_size(unit): - return int(unit['size']) - - result = load_test_vectors_wycheproof(("Protocol", "wycheproof"), - filename, - "Wycheproof HMAC (%s)" % filename, - root_tag={'hash_module': filter_algo}, - unit_tag={'size': filter_size}) - return result - - def setUp(self): - self.tv = [] - self.add_tests("hkdf_sha1_test.json") - self.add_tests("hkdf_sha256_test.json") - self.add_tests("hkdf_sha384_test.json") - self.add_tests("hkdf_sha512_test.json") - - def shortDescription(self): - return self._id - - def warn(self, tv): - if tv.warning and self._wycheproof_warnings: - import warnings - warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment)) - - def test_verify(self, tv): - self._id = "Wycheproof HKDF Test #%d (%s, %s)" % (tv.id, tv.comment, tv.filename) - - try: - key = HKDF(tv.ikm, tv.size, tv.salt, tv.hash_module, 1, tv.info) - except ValueError: - assert not tv.valid - else: - if key != tv.okm: - assert not tv.valid - else: - assert tv.valid - self.warn(tv) - - def runTest(self): - for tv in self.tv: - self.test_verify(tv) - - -def load_hash_by_name(hash_name): - return __import__("Cryptodome.Hash." + hash_name, globals(), locals(), ["new"]) - - -class SP800_180_Counter_Tests(unittest.TestCase): - - def test_negative_zeroes(self): - def prf(s, x): - return HMAC.new(s, x, SHA256).digest() - - self.assertRaises(ValueError, SP800_108_Counter, b'0' * 16, 1, prf, - label=b'A\x00B') - self.assertRaises(ValueError, SP800_108_Counter, b'0' * 16, 1, prf, - context=b'A\x00B') - - def test_multiple_keys(self): - def prf(s, x): - return HMAC.new(s, x, SHA256).digest() - - key = b'0' * 16 - expected = SP800_108_Counter(key, 2*3*23, prf) - for r in (1, 2, 3, 23): - dks = SP800_108_Counter(key, r, prf, 138//r) - self.assertEqual(len(dks), 138//r) - self.assertEqual(len(dks[0]), r) - self.assertEqual(b''.join(dks), expected) - - -def add_tests_sp800_108_counter(cls): - - test_vectors_sp800_108_counter = load_test_vectors(("Protocol", ), - "KDF_SP800_108_COUNTER.txt", - "NIST SP 800 108 KDF Counter Mode", - {'count': lambda x: int(x)}, - ) or [] - - mac_type = None - for idx, tv in enumerate(test_vectors_sp800_108_counter): - - if isinstance(tv, str): - res = re.match(r"\[HMAC-(SHA-[0-9]+)\]", tv) - if res: - hash_name = res.group(1).replace("-", "") - hash_module = load_hash_by_name(hash_name) - mac_type = "hmac" - continue - res = re.match(r"\[CMAC-AES-128\]", tv) - if res: - mac_type = "cmac" - continue - assert res - - if mac_type == "hmac": - def prf(s, x, hash_module=hash_module): - return HMAC.new(s, x, hash_module).digest() - elif mac_type == "cmac": - def prf(s, x, hash_module=hash_module): - return CMAC.new(s, x, AES).digest() - continue - - def kdf_test(self, prf=prf, kin=tv.kin, label=tv.label, - context=tv.context, kout=tv.kout, count=tv.count): - result = SP800_108_Counter(kin, len(kout), prf, 1, label, context) - assert(len(result) == len(kout)) - self.assertEqual(result, kout) - - setattr(cls, "test_kdf_sp800_108_counter_%d" % idx, kdf_test) - - -add_tests_sp800_108_counter(SP800_180_Counter_Tests) - - -def get_tests(config={}): - wycheproof_warnings = config.get('wycheproof_warnings') - - if not config.get('slow_tests'): - PBKDF2_Tests._testData = PBKDF2_Tests._testData[:3] - scrypt_Tests.data = scrypt_Tests.data[:3] - - tests = [] - tests += list_test_cases(PBKDF1_Tests) - tests += list_test_cases(PBKDF2_Tests) - tests += list_test_cases(S2V_Tests) - tests += list_test_cases(HKDF_Tests) - tests += [TestVectorsHKDFWycheproof(wycheproof_warnings)] - tests += list_test_cases(scrypt_Tests) - tests += list_test_cases(bcrypt_Tests) - tests += list_test_cases(SP800_180_Counter_Tests) - - return tests - - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Protocol/test_SecretSharing.py b/resources/lib/deps/Cryptodome/SelfTest/Protocol/test_SecretSharing.py deleted file mode 100644 index 57d97df..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Protocol/test_SecretSharing.py +++ /dev/null @@ -1,267 +0,0 @@ -# -# SelfTest/Protocol/test_secret_sharing.py: Self-test for secret sharing protocols -# -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from unittest import main, TestCase, TestSuite -from binascii import unhexlify, hexlify - -from Cryptodome.Util.py3compat import * -from Cryptodome.SelfTest.st_common import list_test_cases - -from Cryptodome.Protocol.SecretSharing import Shamir, _Element, \ - _mult_gf2, _div_gf2 - -class GF2_Tests(TestCase): - - def test_mult_gf2(self): - # Prove mult by zero - x = _mult_gf2(0,0) - self.assertEqual(x, 0) - - # Prove mult by unity - x = _mult_gf2(34, 1) - self.assertEqual(x, 34) - - z = 3 # (x+1) - y = _mult_gf2(z, z) - self.assertEqual(y, 5) # (x+1)^2 = x^2 + 1 - y = _mult_gf2(y, z) - self.assertEqual(y, 15) # (x+1)^3 = x^3 + x^2 + x + 1 - y = _mult_gf2(y, z) - self.assertEqual(y, 17) # (x+1)^4 = x^4 + 1 - - # Prove linearity works - comps = [1, 4, 128, 2**34] - sum_comps = 1+4+128+2**34 - y = 908 - z = _mult_gf2(sum_comps, y) - w = 0 - for x in comps: - w ^= _mult_gf2(x, y) - self.assertEqual(w, z) - - def test_div_gf2(self): - from Cryptodome.Util.number import size as deg - - x, y = _div_gf2(567, 7) - self.assertTrue(deg(y) < deg(7)) - - w = _mult_gf2(x, 7) ^ y - self.assertEqual(567, w) - - x, y = _div_gf2(7, 567) - self.assertEqual(x, 0) - self.assertEqual(y, 7) - -class Element_Tests(TestCase): - - def test1(self): - # Test encondings - e = _Element(256) - self.assertEqual(int(e), 256) - self.assertEqual(e.encode(), bchr(0)*14 + b("\x01\x00")) - - e = _Element(bchr(0)*14 + b("\x01\x10")) - self.assertEqual(int(e), 0x110) - self.assertEqual(e.encode(), bchr(0)*14 + b("\x01\x10")) - - # Only 16 byte string are a valid encoding - self.assertRaises(ValueError, _Element, bchr(0)) - - def test2(self): - # Test addition - e = _Element(0x10) - f = _Element(0x0A) - self.assertEqual(int(e+f), 0x1A) - - def test3(self): - # Test multiplication - zero = _Element(0) - one = _Element(1) - two = _Element(2) - - x = _Element(6) * zero - self.assertEqual(int(x), 0) - - x = _Element(6) * one - self.assertEqual(int(x), 6) - - x = _Element(2**127) * two - self.assertEqual(int(x), 1 + 2 + 4 + 128) - - def test4(self): - # Test inversion - one = _Element(1) - - x = one.inverse() - self.assertEqual(int(x), 1) - - x = _Element(82323923) - y = x.inverse() - self.assertEqual(int(x * y), 1) - -class Shamir_Tests(TestCase): - - def test1(self): - # Test splitting - shares = Shamir.split(2, 3, bchr(90)*16) - self.assertEqual(len(shares), 3) - for index in range(3): - self.assertEqual(shares[index][0], index+1) - self.assertEqual(len(shares[index][1]), 16) - - def test2(self): - # Test recombine - from itertools import permutations - - test_vectors = ( - (2, "d9fe73909bae28b3757854c0af7ad405", - "1-594ae8964294174d95c33756d2504170", - "2-d897459d29da574eb40e93ec552ffe6e", - "3-5823de9bf0e068b054b5f07a28056b1b", - "4-db2c1f8bff46d748f795da995bd080cb"), - (2, "bf4f902d9a7efafd1f3ffd9291fd5de9", - "1-557bd3b0748064b533469722d1cc7935", - "2-6b2717164783c66d47cd28f2119f14d0", - "3-8113548ba97d58256bb4424251ae300c", - "4-179e9e5a218483ddaeda57539139cf04"), - (3, "ec96aa5c14c9faa699354cf1da74e904", - "1-64579fbf1908d66f7239bf6e2b4e41e1", - "2-6cd9428df8017b52322561e8c672ae3e", - "3-e418776ef5c0579bd9299277374806dd", - "4-ab3f77a0107398d23b323e581bb43f5d", - "5-23fe42431db2b41bd03ecdc7ea8e97ac"), - (3, "44cf249b68b80fcdc27b47be60c2c145", - "1-d6515a3905cd755119b86e311c801e31", - "2-16693d9ac9f10c254036ced5f8917fa3", - "3-84f74338a48476b99bf5e75a84d3a0d1", - "4-3fe8878dc4a5d35811cf3cbcd33dbe52", - "5-ad76f92fa9d0a9c4ca0c1533af7f6132"), - (5, "5398717c982db935d968eebe53a47f5a", - "1-be7be2dd4c068e7ef576aaa1b1c11b01", - "2-f821f5848441cb98b3eb467e2733ee21", - "3-25ee52f53e203f6e29a0297b5ab486b5", - "4-fc9fb58ef74dab947fbf9acd9d5d83cd", - "5-b1949cce46d81552e65f248d3f74cc5c", - "6-d64797f59977c4d4a7956ad916da7699", - "7-ab608a6546a8b9af8820ff832b1135c7"), - (5, "4a78db90fbf35da5545d2fb728e87596", - "1-08daf9a25d8aa184cfbf02b30a0ed6a0", - "2-dda28261e36f0b14168c2cf153fb734e", - "3-e9fdec5505d674a57f9836c417c1ecaa", - "4-4dce5636ae06dee42d2c82e65f06c735", - "5-3963dc118afc2ba798fa1d452b28ef00", - "6-6dfe6ff5b09e94d2f84c382b12f42424", - "7-6faea9d4d4a4e201bf6c90b9000630c3"), - (10, "eccbf6d66d680b49b073c4f1ddf804aa", - "01-7d8ac32fe4ae209ead1f3220fda34466", - "02-f9144e76988aad647d2e61353a6e96d5", - "03-b14c3b80179203363922d60760271c98", - "04-770bb2a8c28f6cee89e00f4d5cc7f861", - "05-6e3d7073ea368334ef67467871c66799", - "06-248792bc74a98ce024477c13c8fb5f8d", - "07-fcea4640d2db820c0604851e293d2487", - "08-2776c36fb714bb1f8525a0be36fc7dba", - "09-6ee7ac8be773e473a4bf75ee5f065762", - "10-33657fc073354cf91d4a68c735aacfc8", - "11-7645c65094a5868bf225c516fdee2d0c", - "12-840485aacb8226631ecd9c70e3018086"), - (10, "377e63bdbb5f7d4dc58a483d035212bb", - "01-32c53260103be431c843b1a633afe3bd", - "02-0107eb16cb8695084d452d2cc50bc7d6", - "03-df1e5c66cd755287fb0446faccd72a06", - "04-361bbcd5d40797f49dfa1898652da197", - "05-160d3ad1512f7dec7fd9344aed318591", - "06-659af6d95df4f25beca4fb9bfee3b7e8", - "07-37f3b208977bad50b3724566b72bfa9d", - "08-6c1de2dfc69c2986142c26a8248eb316", - "09-5e19220837a396bd4bc8cd685ff314c3", - "10-86e7b864fb0f3d628e46d50c1ba92f1c", - "11-065d0082c80b1aea18f4abe0c49df72e", - "12-84a09430c1d20ea9f388f3123c3733a3"), - ) - - def get_share(p): - pos = p.find('-') - return int(p[:pos]), unhexlify(p[pos + 1:]) - - for tv in test_vectors: - k = tv[0] - secret = unhexlify(tv[1]) - max_perms = 10 - for perm, shares_idx in enumerate(permutations(range(2, len(tv)), k)): - if perm > max_perms: - break - shares = [ get_share(tv[x]) for x in shares_idx ] - result = Shamir.combine(shares, True) - self.assertEqual(secret, result) - - def test3(self): - # Loopback split/recombine - secret = unhexlify(b("000102030405060708090a0b0c0d0e0f")) - - shares = Shamir.split(2, 3, secret) - - secret2 = Shamir.combine(shares[:2]) - self.assertEqual(secret, secret2) - - secret3 = Shamir.combine([ shares[0], shares[2] ]) - self.assertEqual(secret, secret3) - - def test4(self): - # Loopback split/recombine (SSSS) - secret = unhexlify(b("000102030405060708090a0b0c0d0e0f")) - - shares = Shamir.split(2, 3, secret, ssss=True) - - secret2 = Shamir.combine(shares[:2], ssss=True) - self.assertEqual(secret, secret2) - - def test5(self): - # Detect duplicate shares - secret = unhexlify(b("000102030405060708090a0b0c0d0e0f")) - - shares = Shamir.split(2, 3, secret) - self.assertRaises(ValueError, Shamir.combine, (shares[0], shares[0])) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(GF2_Tests) - tests += list_test_cases(Element_Tests) - tests += list_test_cases(Shamir_Tests) - return tests - -if __name__ == '__main__': - suite = lambda: TestSuite(get_tests()) - main(defaultTest='suite') - diff --git a/resources/lib/deps/Cryptodome/SelfTest/Protocol/test_ecdh.py b/resources/lib/deps/Cryptodome/SelfTest/Protocol/test_ecdh.py deleted file mode 100644 index 2b280cc..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Protocol/test_ecdh.py +++ /dev/null @@ -1,292 +0,0 @@ -import re -import unittest -from binascii import hexlify - -from Cryptodome.Util.py3compat import bord - -from Cryptodome.Hash import SHA256 -from Cryptodome.PublicKey import ECC -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.SelfTest.loader import load_test_vectors, load_test_vectors_wycheproof - -from Cryptodome.Protocol.DH import key_agreement - - -class FIPS_ECDH_Tests_KAT(unittest.TestCase): - pass - - -test_vectors_verify = load_test_vectors(("Protocol", ), - "KAS_ECC_CDH_PrimitiveTest.txt", - "ECC CDH Primitive (SP800-56A Section 5.7.1.2)", - { - 'qcavsx': lambda x: int(x, 16), - 'qcavsy': lambda x: int(x, 16), - 'diut': lambda x: int(x, 16), - 'qiutx': lambda x: int(x, 16), - 'qiuty': lambda x: int(x, 16), - }) or [] - -for idx, tv in enumerate(test_vectors_verify): - - # Stand-alone header with curve name - if isinstance(tv, str): - res = re.match(r"\[([A-Za-z0-9-]+)\]", tv) - assert res - curve_name = res.group(1) - continue - - public_key = ECC.construct(curve=curve_name, - point_x=tv.qcavsx, - point_y=tv.qcavsy) - - private_key = ECC.construct(curve=curve_name, - d=tv.diut) - - exp_response = tv.ziut - - def ecdh_test(self, - public_key=public_key, - private_key=private_key, - exp_response=exp_response): - z = key_agreement( - static_pub=public_key, - static_priv=private_key, - kdf=lambda x: x) - self.assertEqual(z, exp_response) - - def ecdh_test_rev(self, - public_key=public_key, - private_key=private_key, - exp_response=exp_response): - z = key_agreement( - static_pub=public_key, - static_priv=private_key, - kdf=lambda x: x) - self.assertEqual(z, exp_response) - - setattr(FIPS_ECDH_Tests_KAT, "test_verify_positive_%d" % idx, ecdh_test) - if idx == 1: - setattr(FIPS_ECDH_Tests_KAT, "test_verify_positive_rev_%d" % idx, ecdh_test_rev) - - -class TestVectorsECDHWycheproof(unittest.TestCase): - - desc = "Wycheproof ECDH tests" - - def add_tests(self, filename): - - def curve(g): - return g['curve'] - - def private(u): - return int(u['private'], 16) - - result = load_test_vectors_wycheproof(("Protocol", "wycheproof"), - filename, - "Wycheproof ECDH (%s)" - % filename, - group_tag={'curve': curve}, - unit_tag={'private': private}, - ) - self.tv += result - - def setUp(self): - self.tv = [] - self.desc = None - - self.add_tests("ecdh_secp224r1_ecpoint_test.json") - self.add_tests("ecdh_secp256r1_ecpoint_test.json") - self.add_tests("ecdh_secp384r1_ecpoint_test.json") - self.add_tests("ecdh_secp521r1_ecpoint_test.json") - - self.add_tests("ecdh_secp224r1_test.json") - self.add_tests("ecdh_secp256r1_test.json") - self.add_tests("ecdh_secp384r1_test.json") - self.add_tests("ecdh_secp521r1_test.json") - - def shortDescription(self): - return self.desc - - def test_verify(self, tv): - - if len(tv.public) == 0: - return - - try: - if bord(tv.public[0]) == 4: # SEC1 - public_key = ECC.import_key(tv.public, curve_name=tv.curve) - else: - public_key = ECC.import_key(tv.public) - except ValueError: - assert tv.warning or not tv.valid - return - - private_key = ECC.construct(curve=tv.curve, d=tv.private) - - try: - z = key_agreement(static_pub=public_key, - static_priv=private_key, - kdf=lambda x: x) - except ValueError: - assert not tv.valid - except TypeError as e: - assert not tv.valid - assert "incompatible curve" in str(e) - else: - self.assertEqual(z, tv.shared) - assert tv.valid - - def runTest(self): - for tv in self.tv: - self.desc = "Wycheproof ECDH Verify Test #%d (%s, %s)" % (tv.id, tv.comment, tv.filename) - self.test_verify(tv) - - -class ECDH_Tests(unittest.TestCase): - - static_priv = ECC.import_key('-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg9VHFVKh2a1aVFifH\n+BiyNaRa2kttEg3165Ye/dJxJ7KhRANCAARImIEXro5ZOcyWU2mq/+d79FEZXtTA\nbKkz1aICQXihQdCMzRNbeNtC9LFLzhu1slRKJ2xsDAlw9r6w6vwtkRzr\n-----END PRIVATE KEY-----') - static_pub = ECC.import_key('-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgHhmv8zmZ+Nw8fsZd\ns8tlZflyfw2NE1CRS9DWr3Y3O46hRANCAAS3hZVUCbk+uk3w4S/YOraEVGG+WYpk\nNO/vrwzufUUks2GV2OnBQESe0EBk4Jq8gn4ij8Lvs3rZX2yT+XfeATYd\n-----END PRIVATE KEY-----').public_key() - - eph_priv = ECC.import_key('-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgGPdJmFFFKzLPspIr\nE1T2cEjeIf4ajS9CpneP0e2b3AyhRANCAAQBexAA5BYDcXHs2KOksTYUsst4HhPt\nkp0zkgI2virc3OGJFNGPaCCPfFCQJHwLRaEpiq3SoQlgoBwSc8ZPsl3y\n-----END PRIVATE KEY-----') - - eph_pub = ECC.import_key('-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQghaVZXElSEGEojFKF\nOU0JCpxWUWHvWQUR81gwWrOp76ShRANCAATi1Ib2K+YR3AckD8wxypWef7pw5PRw\ntBaB3RDPyE7IjHZC6yu1DbcXoCdtaw+F5DM+4zpl59n5ZaIy/Yl1BdIy\n-----END PRIVATE KEY-----') - - def test_1(self): - # C(0, 2s) - kdf = lambda x: SHA256.new(x).digest() - z = key_agreement( - kdf=kdf, - static_pub=self.static_pub, - static_priv=self.static_priv) - self.assertEqual(hexlify(z), - b"3960a1101d1193cbaffef4cc7202ebff783c22c6d2e0d5d530ffc66dc197ea9c") - - def test_2(self): - # C(2e, 2s) - kdf = lambda x: SHA256.new(x).digest() - z = key_agreement( - kdf=kdf, - static_pub=self.static_pub, - static_priv=self.static_priv, - eph_pub=self.eph_pub, - eph_priv=self.eph_priv) - self.assertEqual(hexlify(z), - b"7447b733d40c8fab2c633b3dc61e4a8c742f3a6af7e16fb0cc486f5bdb5d6ba2") - - def test_3(self): - # C(1e, 2s) - kdf = lambda x: SHA256.new(x).digest() - z = key_agreement( - kdf=kdf, - static_pub=self.static_pub, - static_priv=self.static_priv, - eph_priv=self.eph_priv) - self.assertEqual(hexlify(z), - b"9e977ae45f33bf67f285d064d83e6632bcafe3a7d33fe571233bab4794ace759") - - def test_4(self): - # C(1e, 2s) - kdf = lambda x: SHA256.new(x).digest() - z = key_agreement( - kdf=kdf, - static_pub=self.static_pub, - static_priv=self.static_priv, - eph_pub=self.eph_pub) - self.assertEqual(hexlify(z), - b"c9532df6aa7e9dbe5fe85da31ee25ff19c179c88691ec4b8328cc2036dcdadf2") - - def test_5(self): - # C(2e, 1s) is not supported - kdf = lambda x: SHA256.new(x).digest() - self.assertRaises(ValueError, - key_agreement, - kdf=kdf, - static_priv=self.static_priv, - eph_pub=self.eph_pub, - eph_priv=self.eph_priv) - - def test_6(self): - # C(2e, 1s) is not supported - kdf = lambda x: SHA256.new(x).digest() - self.assertRaises(ValueError, - key_agreement, - kdf=kdf, - static_pub=self.static_pub, - eph_pub=self.eph_pub, - eph_priv=self.eph_priv) - - def test_7(self): - # C(2e, 0) - kdf = lambda x: SHA256.new(x).digest() - z = key_agreement( - kdf=kdf, - eph_pub=self.eph_pub, - eph_priv=self.eph_priv) - self.assertEqual(hexlify(z), - b"feb257ebe063078b1391aac07913283d7b642ad7df61b46dfc9cd6f420bb896a") - - def test_8(self): - # C(1e, 1s) - kdf = lambda x: SHA256.new(x).digest() - z = key_agreement( - kdf=kdf, - static_priv=self.static_priv, - eph_pub=self.eph_pub) - self.assertEqual(hexlify(z), - b"ee4dc995117476ed57fd17ff0ed44e9f0466d46b929443bc0db9380317583b04") - - def test_9(self): - # C(1e, 1s) - kdf = lambda x: SHA256.new(x).digest() - z = key_agreement( - kdf=kdf, - static_pub=self.static_pub, - eph_priv=self.eph_priv) - self.assertEqual(hexlify(z), - b"2351cc2014f7c40468fa072b5d30f706eeaeef7507311cd8e59bab3b43f03c51") - - def test_10(self): - # No private (local) keys - kdf = lambda x: SHA256.new(x).digest() - self.assertRaises(ValueError, - key_agreement, - kdf=kdf, - static_pub=self.static_pub, - eph_pub=self.eph_pub) - - def test_11(self): - # No public (peer) keys - kdf = lambda x: SHA256.new(x).digest() - self.assertRaises(ValueError, - key_agreement, - kdf=kdf, - static_priv=self.static_priv, - eph_priv=self.eph_priv) - - def test_12(self): - # failure if kdf is missing - self.assertRaises(ValueError, - key_agreement, - static_pub=self.static_pub, - static_priv=self.static_priv) - - -def get_tests(config={}): - - tests = [] - tests += list_test_cases(FIPS_ECDH_Tests_KAT) - tests += [TestVectorsECDHWycheproof()] - tests += list_test_cases(ECDH_Tests) - - slow_tests = config.get('slow_tests') - if slow_tests: - pass - - return tests - - -if __name__ == '__main__': - def suite(): - return unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Protocol/test_rfc1751.py b/resources/lib/deps/Cryptodome/SelfTest/Protocol/test_rfc1751.py deleted file mode 100644 index a79769c..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Protocol/test_rfc1751.py +++ /dev/null @@ -1,62 +0,0 @@ -# -# Test script for Cryptodome.Util.RFC1751. -# -# Part of the Python Cryptography Toolkit -# -# Written by Andrew Kuchling and others -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -__revision__ = "$Id$" - -import binascii -import unittest -from Cryptodome.Util import RFC1751 -from Cryptodome.Util.py3compat import * - -test_data = [('EB33F77EE73D4053', 'TIDE ITCH SLOW REIN RULE MOT'), - ('CCAC2AED591056BE4F90FD441C534766', - 'RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE'), - ('EFF81F9BFBC65350920CDD7416DE8009', - 'TROD MUTE TAIL WARM CHAR KONG HAAG CITY BORE O TEAL AWL') - ] - -class RFC1751Test_k2e (unittest.TestCase): - - def runTest (self): - "Check converting keys to English" - for key, words in test_data: - key=binascii.a2b_hex(b(key)) - self.assertEqual(RFC1751.key_to_english(key), words) - -class RFC1751Test_e2k (unittest.TestCase): - - def runTest (self): - "Check converting English strings to keys" - for key, words in test_data: - key=binascii.a2b_hex(b(key)) - self.assertEqual(RFC1751.english_to_key(words), key) - -# class RFC1751Test - -def get_tests(config={}): - return [RFC1751Test_k2e(), RFC1751Test_e2k()] - -if __name__ == "__main__": - unittest.main() diff --git a/resources/lib/deps/Cryptodome/SelfTest/PublicKey/__init__.py b/resources/lib/deps/Cryptodome/SelfTest/PublicKey/__init__.py deleted file mode 100644 index 7cdd320..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/PublicKey/__init__.py +++ /dev/null @@ -1,53 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/PublicKey/__init__.py: Self-test for public key crypto -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test for public-key crypto""" - -import unittest -from Cryptodome.SelfTest.PublicKey import (test_DSA, test_RSA, - test_ECC_NIST, test_ECC_25519, test_ECC_448, - test_import_DSA, test_import_RSA, - test_import_ECC, test_ElGamal) - - -def get_tests(config={}): - tests = [] - tests += test_DSA.get_tests(config=config) - tests += test_RSA.get_tests(config=config) - tests += test_ECC_NIST.get_tests(config=config) - tests += test_ECC_25519.get_tests(config=config) - tests += test_ECC_448.get_tests(config=config) - - tests += test_import_DSA.get_tests(config=config) - tests += test_import_RSA.get_tests(config=config) - tests += test_import_ECC.get_tests(config=config) - - tests += test_ElGamal.get_tests(config=config) - return tests - - -if __name__ == '__main__': - def suite(): - return unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_DSA.py b/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_DSA.py deleted file mode 100644 index 160d882..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_DSA.py +++ /dev/null @@ -1,247 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/PublicKey/test_DSA.py: Self-test for the DSA primitive -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.PublicKey.DSA""" - -import os -from Cryptodome.Util.py3compat import * - -import unittest -from Cryptodome.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex - -def _sws(s): - """Remove whitespace from a text or byte string""" - if isinstance(s,str): - return "".join(s.split()) - else: - return b("").join(s.split()) - -class DSATest(unittest.TestCase): - # Test vector from "Appendix 5. Example of the DSA" of - # "Digital Signature Standard (DSS)", - # U.S. Department of Commerce/National Institute of Standards and Technology - # FIPS 186-2 (+Change Notice), 2000 January 27. - # http://csrc.nist.gov/publications/fips/fips186-2/fips186-2-change1.pdf - - y = _sws("""19131871 d75b1612 a819f29d 78d1b0d7 346f7aa7 7bb62a85 - 9bfd6c56 75da9d21 2d3a36ef 1672ef66 0b8c7c25 5cc0ec74 - 858fba33 f44c0669 9630a76b 030ee333""") - - g = _sws("""626d0278 39ea0a13 413163a5 5b4cb500 299d5522 956cefcb - 3bff10f3 99ce2c2e 71cb9de5 fa24babf 58e5b795 21925c9c - c42e9f6f 464b088c c572af53 e6d78802""") - - p = _sws("""8df2a494 492276aa 3d25759b b06869cb eac0d83a fb8d0cf7 - cbb8324f 0d7882e5 d0762fc5 b7210eaf c2e9adac 32ab7aac - 49693dfb f83724c2 ec0736ee 31c80291""") - - q = _sws("""c773218c 737ec8ee 993b4f2d ed30f48e dace915f""") - - x = _sws("""2070b322 3dba372f de1c0ffc 7b2e3b49 8b260614""") - - k = _sws("""358dad57 1462710f 50e254cf 1a376b2b deaadfbf""") - k_inverse = _sws("""0d516729 8202e49b 4116ac10 4fc3f415 ae52f917""") - m = b2a_hex(b("abc")) - m_hash = _sws("""a9993e36 4706816a ba3e2571 7850c26c 9cd0d89d""") - r = _sws("""8bac1ab6 6410435c b7181f95 b16ab97c 92b341c0""") - s = _sws("""41e2345f 1f56df24 58f426d1 55b4ba2d b6dcd8c8""") - - def setUp(self): - global DSA, Random, bytes_to_long, size - from Cryptodome.PublicKey import DSA - from Cryptodome import Random - from Cryptodome.Util.number import bytes_to_long, inverse, size - - self.dsa = DSA - - def test_generate_1arg(self): - """DSA (default implementation) generated key (1 argument)""" - dsaObj = self.dsa.generate(1024) - self._check_private_key(dsaObj) - pub = dsaObj.public_key() - self._check_public_key(pub) - - def test_generate_2arg(self): - """DSA (default implementation) generated key (2 arguments)""" - dsaObj = self.dsa.generate(1024, Random.new().read) - self._check_private_key(dsaObj) - pub = dsaObj.public_key() - self._check_public_key(pub) - - def test_construct_4tuple(self): - """DSA (default implementation) constructed key (4-tuple)""" - (y, g, p, q) = [bytes_to_long(a2b_hex(param)) for param in (self.y, self.g, self.p, self.q)] - dsaObj = self.dsa.construct((y, g, p, q)) - self._test_verification(dsaObj) - - def test_construct_5tuple(self): - """DSA (default implementation) constructed key (5-tuple)""" - (y, g, p, q, x) = [bytes_to_long(a2b_hex(param)) for param in (self.y, self.g, self.p, self.q, self.x)] - dsaObj = self.dsa.construct((y, g, p, q, x)) - self._test_signing(dsaObj) - self._test_verification(dsaObj) - - def test_construct_bad_key4(self): - (y, g, p, q) = [bytes_to_long(a2b_hex(param)) for param in (self.y, self.g, self.p, self.q)] - tup = (y, g, p+1, q) - self.assertRaises(ValueError, self.dsa.construct, tup) - - tup = (y, g, p, q+1) - self.assertRaises(ValueError, self.dsa.construct, tup) - - tup = (y, 1, p, q) - self.assertRaises(ValueError, self.dsa.construct, tup) - - def test_construct_bad_key5(self): - (y, g, p, q, x) = [bytes_to_long(a2b_hex(param)) for param in (self.y, self.g, self.p, self.q, self.x)] - tup = (y, g, p, q, x+1) - self.assertRaises(ValueError, self.dsa.construct, tup) - - tup = (y, g, p, q, q+10) - self.assertRaises(ValueError, self.dsa.construct, tup) - - def _check_private_key(self, dsaObj): - # Check capabilities - self.assertEqual(1, dsaObj.has_private()) - self.assertEqual(1, dsaObj.can_sign()) - self.assertEqual(0, dsaObj.can_encrypt()) - - # Sanity check key data - self.assertEqual(1, dsaObj.p > dsaObj.q) # p > q - self.assertEqual(160, size(dsaObj.q)) # size(q) == 160 bits - self.assertEqual(0, (dsaObj.p - 1) % dsaObj.q) # q is a divisor of p-1 - self.assertEqual(dsaObj.y, pow(dsaObj.g, dsaObj.x, dsaObj.p)) # y == g**x mod p - self.assertEqual(1, 0 < dsaObj.x < dsaObj.q) # 0 < x < q - - def _check_public_key(self, dsaObj): - k = bytes_to_long(a2b_hex(self.k)) - m_hash = bytes_to_long(a2b_hex(self.m_hash)) - - # Check capabilities - self.assertEqual(0, dsaObj.has_private()) - self.assertEqual(1, dsaObj.can_sign()) - self.assertEqual(0, dsaObj.can_encrypt()) - - # Check that private parameters are all missing - self.assertEqual(0, hasattr(dsaObj, 'x')) - - # Sanity check key data - self.assertEqual(1, dsaObj.p > dsaObj.q) # p > q - self.assertEqual(160, size(dsaObj.q)) # size(q) == 160 bits - self.assertEqual(0, (dsaObj.p - 1) % dsaObj.q) # q is a divisor of p-1 - - # Public-only key objects should raise an error when .sign() is called - self.assertRaises(TypeError, dsaObj._sign, m_hash, k) - - # Check __eq__ and __ne__ - self.assertEqual(dsaObj.public_key() == dsaObj.public_key(),True) # assert_ - self.assertEqual(dsaObj.public_key() != dsaObj.public_key(),False) # assertFalse - - self.assertEqual(dsaObj.public_key(), dsaObj.publickey()) - - def _test_signing(self, dsaObj): - k = bytes_to_long(a2b_hex(self.k)) - m_hash = bytes_to_long(a2b_hex(self.m_hash)) - r = bytes_to_long(a2b_hex(self.r)) - s = bytes_to_long(a2b_hex(self.s)) - (r_out, s_out) = dsaObj._sign(m_hash, k) - self.assertEqual((r, s), (r_out, s_out)) - - def _test_verification(self, dsaObj): - m_hash = bytes_to_long(a2b_hex(self.m_hash)) - r = bytes_to_long(a2b_hex(self.r)) - s = bytes_to_long(a2b_hex(self.s)) - self.assertTrue(dsaObj._verify(m_hash, (r, s))) - self.assertFalse(dsaObj._verify(m_hash + 1, (r, s))) - - def test_repr(self): - (y, g, p, q) = [bytes_to_long(a2b_hex(param)) for param in (self.y, self.g, self.p, self.q)] - dsaObj = self.dsa.construct((y, g, p, q)) - repr(dsaObj) - - -class DSADomainTest(unittest.TestCase): - - def test_domain1(self): - """Verify we can generate new keys in a given domain""" - dsa_key_1 = DSA.generate(1024) - domain_params = dsa_key_1.domain() - - dsa_key_2 = DSA.generate(1024, domain=domain_params) - self.assertEqual(dsa_key_1.p, dsa_key_2.p) - self.assertEqual(dsa_key_1.q, dsa_key_2.q) - self.assertEqual(dsa_key_1.g, dsa_key_2.g) - - self.assertEqual(dsa_key_1.domain(), dsa_key_2.domain()) - - def _get_weak_domain(self): - - from Cryptodome.Math.Numbers import Integer - from Cryptodome.Math import Primality - - p = Integer(4) - while p.size_in_bits() != 1024 or Primality.test_probable_prime(p) != Primality.PROBABLY_PRIME: - q1 = Integer.random(exact_bits=80) - q2 = Integer.random(exact_bits=80) - q = q1 * q2 - z = Integer.random(exact_bits=1024-160) - p = z * q + 1 - - h = Integer(2) - g = 1 - while g == 1: - g = pow(h, z, p) - h += 1 - - return (p, q, g) - - - def test_generate_error_weak_domain(self): - """Verify that domain parameters with composite q are rejected""" - - domain_params = self._get_weak_domain() - self.assertRaises(ValueError, DSA.generate, 1024, domain=domain_params) - - - def test_construct_error_weak_domain(self): - """Verify that domain parameters with composite q are rejected""" - - from Cryptodome.Math.Numbers import Integer - - p, q, g = self._get_weak_domain() - y = pow(g, 89, p) - self.assertRaises(ValueError, DSA.construct, (y, g, p, q)) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(DSATest) - tests += list_test_cases(DSADomainTest) - return tests - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_ECC_25519.py b/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_ECC_25519.py deleted file mode 100644 index 1362e58..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_ECC_25519.py +++ /dev/null @@ -1,333 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2022, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import unittest -from binascii import unhexlify - -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.SelfTest.loader import load_test_vectors - -from Cryptodome.PublicKey import ECC -from Cryptodome.PublicKey.ECC import EccPoint, _curves, EccKey - -from Cryptodome.Math.Numbers import Integer - -from Cryptodome.Hash import SHAKE128 - - -class TestEccPoint_Ed25519(unittest.TestCase): - - Gxy = {"x": 15112221349535400772501151409588531511454012693041857206046113283949847762202, - "y": 46316835694926478169428394003475163141307993866256225615783033603165251855960} - - G2xy = {"x": 24727413235106541002554574571675588834622768167397638456726423682521233608206, - "y": 15549675580280190176352668710449542251549572066445060580507079593062643049417} - - G3xy = {"x": 46896733464454938657123544595386787789046198280132665686241321779790909858396, - "y": 8324843778533443976490377120369201138301417226297555316741202210403726505172} - - pointG = EccPoint(Gxy['x'], Gxy['y'], curve="Ed25519") - pointG2 = EccPoint(G2xy['x'], G2xy['y'], curve="Ed25519") - pointG3 = EccPoint(G3xy['x'], G3xy['y'], curve="Ed25519") - - def test_init_xy(self): - EccPoint(self.Gxy['x'], self.Gxy['y'], curve="Ed25519") - - # Neutral point - pai = EccPoint(0, 1, curve="Ed25519") - self.assertEqual(pai.x, 0) - self.assertEqual(pai.y, 1) - self.assertEqual(pai.xy, (0, 1)) - - # G - bp = self.pointG.copy() - self.assertEqual(bp.x, 15112221349535400772501151409588531511454012693041857206046113283949847762202) - self.assertEqual(bp.y, 46316835694926478169428394003475163141307993866256225615783033603165251855960) - self.assertEqual(bp.xy, (bp.x, bp.y)) - - # 2G - bp2 = self.pointG2.copy() - self.assertEqual(bp2.x, 24727413235106541002554574571675588834622768167397638456726423682521233608206) - self.assertEqual(bp2.y, 15549675580280190176352668710449542251549572066445060580507079593062643049417) - self.assertEqual(bp2.xy, (bp2.x, bp2.y)) - - # 5G - EccPoint(x=33467004535436536005251147249499675200073690106659565782908757308821616914995, - y=43097193783671926753355113395909008640284023746042808659097434958891230611693, - curve="Ed25519") - - # Catch if point is not on the curve - self.assertRaises(ValueError, EccPoint, 34, 35, curve="Ed25519") - - def test_set(self): - pointW = EccPoint(0, 1, curve="Ed25519") - pointW.set(self.pointG) - self.assertEqual(pointW.x, self.pointG.x) - self.assertEqual(pointW.y, self.pointG.y) - - def test_copy(self): - pointW = self.pointG.copy() - self.assertEqual(pointW.x, self.pointG.x) - self.assertEqual(pointW.y, self.pointG.y) - - def test_equal(self): - pointH = self.pointG.copy() - pointI = self.pointG2.copy() - self.assertEqual(self.pointG, pointH) - self.assertNotEqual(self.pointG, pointI) - - def test_pai(self): - pai = EccPoint(0, 1, curve="Ed25519") - self.assertTrue(pai.is_point_at_infinity()) - self.assertEqual(pai, pai.point_at_infinity()) - - def test_negate(self): - negG = -self.pointG - sum = self.pointG + negG - self.assertTrue(sum.is_point_at_infinity()) - - def test_addition(self): - self.assertEqual(self.pointG + self.pointG2, self.pointG3) - self.assertEqual(self.pointG2 + self.pointG, self.pointG3) - self.assertEqual(self.pointG2 + self.pointG.point_at_infinity(), self.pointG2) - self.assertEqual(self.pointG.point_at_infinity() + self.pointG2, self.pointG2) - - G5 = self.pointG2 + self.pointG3 - self.assertEqual(G5.x, 33467004535436536005251147249499675200073690106659565782908757308821616914995) - self.assertEqual(G5.y, 43097193783671926753355113395909008640284023746042808659097434958891230611693) - - def test_inplace_addition(self): - pointH = self.pointG.copy() - pointH += self.pointG - self.assertEqual(pointH, self.pointG2) - pointH += self.pointG - self.assertEqual(pointH, self.pointG3) - pointH += self.pointG.point_at_infinity() - self.assertEqual(pointH, self.pointG3) - - def test_doubling(self): - pointH = self.pointG.copy() - pointH.double() - self.assertEqual(pointH.x, self.pointG2.x) - self.assertEqual(pointH.y, self.pointG2.y) - - # 2*0 - pai = self.pointG.point_at_infinity() - pointR = pai.copy() - pointR.double() - self.assertEqual(pointR, pai) - - def test_scalar_multiply(self): - d = 0 - pointH = d * self.pointG - self.assertEqual(pointH.x, 0) - self.assertEqual(pointH.y, 1) - - d = 1 - pointH = d * self.pointG - self.assertEqual(pointH.x, self.pointG.x) - self.assertEqual(pointH.y, self.pointG.y) - - d = 2 - pointH = d * self.pointG - self.assertEqual(pointH.x, self.pointG2.x) - self.assertEqual(pointH.y, self.pointG2.y) - - d = 3 - pointH = d * self.pointG - self.assertEqual(pointH.x, self.pointG3.x) - self.assertEqual(pointH.y, self.pointG3.y) - - d = 4 - pointH = d * self.pointG - self.assertEqual(pointH.x, 14582954232372986451776170844943001818709880559417862259286374126315108956272) - self.assertEqual(pointH.y, 32483318716863467900234833297694612235682047836132991208333042722294373421359) - - d = 5 - pointH = d * self.pointG - self.assertEqual(pointH.x, 33467004535436536005251147249499675200073690106659565782908757308821616914995) - self.assertEqual(pointH.y, 43097193783671926753355113395909008640284023746042808659097434958891230611693) - - d = 10 - pointH = d * self.pointG - self.assertEqual(pointH.x, 43500613248243327786121022071801015118933854441360174117148262713429272820047) - self.assertEqual(pointH.y, 45005105423099817237495816771148012388779685712352441364231470781391834741548) - - d = 20 - pointH = d * self.pointG - self.assertEqual(pointH.x, 46694936775300686710656303283485882876784402425210400817529601134760286812591) - self.assertEqual(pointH.y, 8786390172762935853260670851718824721296437982862763585171334833968259029560) - - d = 255 - pointH = d * self.pointG - self.assertEqual(pointH.x, 36843863416400016952258312492144504209624961884991522125275155377549541182230) - self.assertEqual(pointH.y, 22327030283879720808995671630924669697661065034121040761798775626517750047180) - - d = 256 - pointH = d * self.pointG - self.assertEqual(pointH.x, 42740085206947573681423002599456489563927820004573071834350074001818321593686) - self.assertEqual(pointH.y, 6935684722522267618220753829624209639984359598320562595061366101608187623111) - - def test_sizes(self): - self.assertEqual(self.pointG.size_in_bits(), 255) - self.assertEqual(self.pointG.size_in_bytes(), 32) - - -class TestEccKey_Ed25519(unittest.TestCase): - - def test_private_key(self): - seed = unhexlify("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60") - Px = 38815646466658113194383306759739515082307681141926459231621296960732224964046 - Py = 11903303657706407974989296177215005343713679411332034699907763981919547054807 - - key = EccKey(curve="Ed25519", seed=seed) - self.assertEqual(key.seed, seed) - self.assertEqual(key.d, 36144925721603087658594284515452164870581325872720374094707712194495455132720) - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ.x, Px) - self.assertEqual(key.pointQ.y, Py) - - point = EccPoint(Px, Py, "ed25519") - key = EccKey(curve="Ed25519", seed=seed, point=point) - self.assertEqual(key.d, 36144925721603087658594284515452164870581325872720374094707712194495455132720) - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ, point) - - # Other names - key = EccKey(curve="ed25519", seed=seed) - - # Must not accept d parameter - self.assertRaises(ValueError, EccKey, curve="ed25519", d=1) - - def test_public_key(self): - point = EccPoint(_curves['ed25519'].Gx, _curves['ed25519'].Gy, curve='ed25519') - key = EccKey(curve="ed25519", point=point) - self.assertFalse(key.has_private()) - self.assertEqual(key.pointQ, point) - - def test_public_key_derived(self): - priv_key = EccKey(curve="ed25519", seed=b'H'*32) - pub_key = priv_key.public_key() - self.assertFalse(pub_key.has_private()) - self.assertEqual(priv_key.pointQ, pub_key.pointQ) - - def test_invalid_seed(self): - self.assertRaises(ValueError, lambda: EccKey(curve="ed25519", seed=b'H' * 31)) - - def test_equality(self): - private_key = ECC.construct(seed=b'H'*32, curve="Ed25519") - private_key2 = ECC.construct(seed=b'H'*32, curve="ed25519") - private_key3 = ECC.construct(seed=b'C'*32, curve="Ed25519") - - public_key = private_key.public_key() - public_key2 = private_key2.public_key() - public_key3 = private_key3.public_key() - - self.assertEqual(private_key, private_key2) - self.assertNotEqual(private_key, private_key3) - - self.assertEqual(public_key, public_key2) - self.assertNotEqual(public_key, public_key3) - - self.assertNotEqual(public_key, private_key) - - def test_name_consistency(self): - key = ECC.generate(curve='ed25519') - self.assertIn("curve='Ed25519'", repr(key)) - self.assertEqual(key.curve, 'Ed25519') - self.assertEqual(key.public_key().curve, 'Ed25519') - - -class TestEccModule_Ed25519(unittest.TestCase): - - def test_generate(self): - key = ECC.generate(curve="Ed25519") - self.assertTrue(key.has_private()) - point = EccPoint(_curves['Ed25519'].Gx, _curves['Ed25519'].Gy, curve="Ed25519") * key.d - self.assertEqual(key.pointQ, point) - - # Always random - key2 = ECC.generate(curve="Ed25519") - self.assertNotEqual(key, key2) - - # Other names - ECC.generate(curve="Ed25519") - - # Random source - key1 = ECC.generate(curve="Ed25519", randfunc=SHAKE128.new().read) - key2 = ECC.generate(curve="Ed25519", randfunc=SHAKE128.new().read) - self.assertEqual(key1, key2) - - def test_construct(self): - seed = unhexlify("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60") - Px = 38815646466658113194383306759739515082307681141926459231621296960732224964046 - Py = 11903303657706407974989296177215005343713679411332034699907763981919547054807 - d = 36144925721603087658594284515452164870581325872720374094707712194495455132720 - point = EccPoint(Px, Py, curve="Ed25519") - - # Private key only - key = ECC.construct(curve="Ed25519", seed=seed) - self.assertEqual(key.pointQ, point) - self.assertTrue(key.has_private()) - - # Public key only - key = ECC.construct(curve="Ed25519", point_x=Px, point_y=Py) - self.assertEqual(key.pointQ, point) - self.assertFalse(key.has_private()) - - # Private and public key - key = ECC.construct(curve="Ed25519", seed=seed, point_x=Px, point_y=Py) - self.assertEqual(key.pointQ, point) - self.assertTrue(key.has_private()) - - # Other names - key = ECC.construct(curve="ed25519", seed=seed) - - def test_negative_construct(self): - coord = dict(point_x=10, point_y=4) - coordG = dict(point_x=_curves['ed25519'].Gx, point_y=_curves['ed25519'].Gy) - - self.assertRaises(ValueError, ECC.construct, curve="Ed25519", **coord) - self.assertRaises(ValueError, ECC.construct, curve="Ed25519", d=2, **coordG) - self.assertRaises(ValueError, ECC.construct, curve="Ed25519", seed=b'H'*31) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(TestEccPoint_Ed25519) - tests += list_test_cases(TestEccKey_Ed25519) - tests += list_test_cases(TestEccModule_Ed25519) - return tests - - -if __name__ == '__main__': - def suite(): - return unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_ECC_448.py b/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_ECC_448.py deleted file mode 100644 index fbebaea..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_ECC_448.py +++ /dev/null @@ -1,333 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2022, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import unittest -from binascii import unhexlify - -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.SelfTest.loader import load_test_vectors - -from Cryptodome.PublicKey import ECC -from Cryptodome.PublicKey.ECC import EccPoint, _curves, EccKey - -from Cryptodome.Math.Numbers import Integer - -from Cryptodome.Hash import SHAKE128 - - -class TestEccPoint_Ed448(unittest.TestCase): - - Gxy = {"x": 0x4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e, - "y": 0x693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14} - - G2xy = {"x": 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa955555555555555555555555555555555555555555555555555555555, - "y": 0xae05e9634ad7048db359d6205086c2b0036ed7a035884dd7b7e36d728ad8c4b80d6565833a2a3098bbbcb2bed1cda06bdaeafbcdea9386ed} - - G3xy = {"x": 0x865886b9108af6455bd64316cb6943332241b8b8cda82c7e2ba077a4a3fcfe8daa9cbf7f6271fd6e862b769465da8575728173286ff2f8f, - "y": 0xe005a8dbd5125cf706cbda7ad43aa6449a4a8d952356c3b9fce43c82ec4e1d58bb3a331bdb6767f0bffa9a68fed02dafb822ac13588ed6fc} - - pointG = EccPoint(Gxy['x'], Gxy['y'], curve="Ed448") - pointG2 = EccPoint(G2xy['x'], G2xy['y'], curve="Ed448") - pointG3 = EccPoint(G3xy['x'], G3xy['y'], curve="Ed448") - - def test_init_xy(self): - EccPoint(self.Gxy['x'], self.Gxy['y'], curve="Ed448") - - # Neutral point - pai = EccPoint(0, 1, curve="Ed448") - self.assertEqual(pai.x, 0) - self.assertEqual(pai.y, 1) - self.assertEqual(pai.xy, (0, 1)) - - # G - bp = self.pointG.copy() - self.assertEqual(bp.x, 0x4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e) - self.assertEqual(bp.y, 0x693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14) - self.assertEqual(bp.xy, (bp.x, bp.y)) - - # 2G - bp2 = self.pointG2.copy() - self.assertEqual(bp2.x, 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa955555555555555555555555555555555555555555555555555555555) - self.assertEqual(bp2.y, 0xae05e9634ad7048db359d6205086c2b0036ed7a035884dd7b7e36d728ad8c4b80d6565833a2a3098bbbcb2bed1cda06bdaeafbcdea9386ed) - self.assertEqual(bp2.xy, (bp2.x, bp2.y)) - - # 5G - EccPoint(x=0x7a9f9335a48dcb0e2ba7601eedb50def80cbcf728562ada756d761e8958812808bc0d57a920c3c96f07b2d8cefc6f950d0a99d1092030034, - y=0xadfd751a2517edd3b9109ce4fd580ade260ca1823ab18fced86551f7b698017127d7a4ee59d2b33c58405512881f225443b4731472f435eb, - curve="Ed448") - - # Catch if point is not on the curve - self.assertRaises(ValueError, EccPoint, 34, 35, curve="Ed448") - - def test_set(self): - pointW = EccPoint(0, 1, curve="Ed448") - pointW.set(self.pointG) - self.assertEqual(pointW.x, self.pointG.x) - self.assertEqual(pointW.y, self.pointG.y) - - def test_copy(self): - pointW = self.pointG.copy() - self.assertEqual(pointW.x, self.pointG.x) - self.assertEqual(pointW.y, self.pointG.y) - - def test_equal(self): - pointH = self.pointG.copy() - pointI = self.pointG2.copy() - self.assertEqual(self.pointG, pointH) - self.assertNotEqual(self.pointG, pointI) - - def test_pai(self): - pai = EccPoint(0, 1, curve="Ed448") - self.assertTrue(pai.is_point_at_infinity()) - self.assertEqual(pai, pai.point_at_infinity()) - - def test_negate(self): - negG = -self.pointG - sum = self.pointG + negG - self.assertTrue(sum.is_point_at_infinity()) - - def test_addition(self): - self.assertEqual(self.pointG + self.pointG2, self.pointG3) - self.assertEqual(self.pointG2 + self.pointG, self.pointG3) - self.assertEqual(self.pointG2 + self.pointG.point_at_infinity(), self.pointG2) - self.assertEqual(self.pointG.point_at_infinity() + self.pointG2, self.pointG2) - - G5 = self.pointG2 + self.pointG3 - self.assertEqual(G5.x, 0x7a9f9335a48dcb0e2ba7601eedb50def80cbcf728562ada756d761e8958812808bc0d57a920c3c96f07b2d8cefc6f950d0a99d1092030034) - self.assertEqual(G5.y, 0xadfd751a2517edd3b9109ce4fd580ade260ca1823ab18fced86551f7b698017127d7a4ee59d2b33c58405512881f225443b4731472f435eb) - - def test_inplace_addition(self): - pointH = self.pointG.copy() - pointH += self.pointG - self.assertEqual(pointH, self.pointG2) - pointH += self.pointG - self.assertEqual(pointH, self.pointG3) - pointH += self.pointG.point_at_infinity() - self.assertEqual(pointH, self.pointG3) - - def test_doubling(self): - pointH = self.pointG.copy() - pointH.double() - self.assertEqual(pointH.x, self.pointG2.x) - self.assertEqual(pointH.y, self.pointG2.y) - - # 2*0 - pai = self.pointG.point_at_infinity() - pointR = pai.copy() - pointR.double() - self.assertEqual(pointR, pai) - - def test_scalar_multiply(self): - d = 0 - pointH = d * self.pointG - self.assertEqual(pointH.x, 0) - self.assertEqual(pointH.y, 1) - - d = 1 - pointH = d * self.pointG - self.assertEqual(pointH.x, self.pointG.x) - self.assertEqual(pointH.y, self.pointG.y) - - d = 2 - pointH = d * self.pointG - self.assertEqual(pointH.x, self.pointG2.x) - self.assertEqual(pointH.y, self.pointG2.y) - - d = 3 - pointH = d * self.pointG - self.assertEqual(pointH.x, self.pointG3.x) - self.assertEqual(pointH.y, self.pointG3.y) - - d = 4 - pointH = d * self.pointG - self.assertEqual(pointH.x, 0x49dcbc5c6c0cce2c1419a17226f929ea255a09cf4e0891c693fda4be70c74cc301b7bdf1515dd8ba21aee1798949e120e2ce42ac48ba7f30) - self.assertEqual(pointH.y, 0xd49077e4accde527164b33a5de021b979cb7c02f0457d845c90dc3227b8a5bc1c0d8f97ea1ca9472b5d444285d0d4f5b32e236f86de51839) - - d = 5 - pointH = d * self.pointG - self.assertEqual(pointH.x, 0x7a9f9335a48dcb0e2ba7601eedb50def80cbcf728562ada756d761e8958812808bc0d57a920c3c96f07b2d8cefc6f950d0a99d1092030034) - self.assertEqual(pointH.y, 0xadfd751a2517edd3b9109ce4fd580ade260ca1823ab18fced86551f7b698017127d7a4ee59d2b33c58405512881f225443b4731472f435eb) - - d = 10 - pointH = d * self.pointG - self.assertEqual(pointH.x, 0x77486f9d19f6411cdd35d30d1c3235f71936452c787e5c034134d3e8172278aca61622bc805761ce3dab65118a0122d73b403165d0ed303d) - self.assertEqual(pointH.y, 0x4d2fea0b026be11024f1f0fe7e94e618e8ac17381ada1d1bf7ee293a68ff5d0bf93c1997dc1aabdc0c7e6381428d85b6b1954a89e4cddf67) - - d = 20 - pointH = d * self.pointG - self.assertEqual(pointH.x, 0x3c236422354600fe6763defcc1503737e4ed89e262d0de3ec1e552020f2a56fe3b9e1e012d021072598c3c2821e18268bb8fb8339c0d1216) - self.assertEqual(pointH.y, 0xb555b9721f630ccb05fc466de4c74d3d2781e69eca88e1b040844f04cab39fd946f91c688fa42402bb38fb9c3e61231017020b219b4396e1) - - d = 255 - pointH = d * self.pointG - self.assertEqual(pointH.x, 0xbeb7f8388b05cd9c1aa2e3c0dcf31e2b563659361826225390e7748654f627d5c36cbe627e9019936b56d15d4dad7c337c09bac64ff4197f) - self.assertEqual(pointH.y, 0x1e37312b2dd4e9440c43c6e7725fc4fa3d11e582d4863f1d018e28f50c0efdb1f53f9b01ada7c87fa162b1f0d72401015d57613d25f1ad53) - - d = 256 - pointH = d * self.pointG - self.assertEqual(pointH.x, 0xf19c34feb56730e3e2be761ac0a2a2b24853b281dda019fc35a5ab58e3696beb39609ae756b0d20fb7ccf0d79aaf5f3bca2e4fdb25bfac1c) - self.assertEqual(pointH.y, 0x3beb69cc9111bffcaddc61d363ce6fe5dd44da4aadce78f52e92e985d5442344ced72c4611ed0daac9f4f5661eab73d7a12d25ce8a30241e) - - def test_sizes(self): - self.assertEqual(self.pointG.size_in_bits(), 448) - self.assertEqual(self.pointG.size_in_bytes(), 56) - - -class TestEccKey_Ed448(unittest.TestCase): - - def test_private_key(self): - seed = unhexlify("4adf5d37ac6785e83e99a924f92676d366a78690af59c92b6bdf14f9cdbcf26fdad478109607583d633b60078d61d51d81b7509c5433b0d4c9") - Px = 0x72a01eea003a35f9ac44231dc4aae2a382f351d80bf32508175b0855edcf389aa2bbf308dd961ce361a6e7c2091bc78957f6ebcf3002a617 - Py = 0x9e0d08d84586e9aeefecacb41d049b831f1a3ee0c3eada63e34557b30702b50ab59fb372feff7c30b8cbb7dd51afbe88444ec56238722ec1 - - key = EccKey(curve="Ed448", seed=seed) - self.assertEqual(key.seed, seed) - self.assertEqual(key.d, 0xb07cf179604f83433186e5178760c759c15125ee54ff6f8dcde46e872b709ac82ed0bd0a4e036d774034dcb18a9fb11894657a1485895f80) - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ.x, Px) - self.assertEqual(key.pointQ.y, Py) - - point = EccPoint(Px, Py, "ed448") - key = EccKey(curve="Ed448", seed=seed, point=point) - self.assertEqual(key.d, 0xb07cf179604f83433186e5178760c759c15125ee54ff6f8dcde46e872b709ac82ed0bd0a4e036d774034dcb18a9fb11894657a1485895f80) - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ, point) - - # Other names - key = EccKey(curve="ed448", seed=seed) - - # Must not accept d parameter - self.assertRaises(ValueError, EccKey, curve="ed448", d=1) - - def test_public_key(self): - point = EccPoint(_curves['ed448'].Gx, _curves['ed448'].Gy, curve='ed448') - key = EccKey(curve="ed448", point=point) - self.assertFalse(key.has_private()) - self.assertEqual(key.pointQ, point) - - def test_public_key_derived(self): - priv_key = EccKey(curve="ed448", seed=b'H'*57) - pub_key = priv_key.public_key() - self.assertFalse(pub_key.has_private()) - self.assertEqual(priv_key.pointQ, pub_key.pointQ) - - def test_invalid_seed(self): - self.assertRaises(ValueError, lambda: EccKey(curve="ed448", seed=b'H' * 56)) - - def test_equality(self): - private_key = ECC.construct(seed=b'H'*57, curve="Ed448") - private_key2 = ECC.construct(seed=b'H'*57, curve="ed448") - private_key3 = ECC.construct(seed=b'C'*57, curve="Ed448") - - public_key = private_key.public_key() - public_key2 = private_key2.public_key() - public_key3 = private_key3.public_key() - - self.assertEqual(private_key, private_key2) - self.assertNotEqual(private_key, private_key3) - - self.assertEqual(public_key, public_key2) - self.assertNotEqual(public_key, public_key3) - - self.assertNotEqual(public_key, private_key) - - def test_name_consistency(self): - key = ECC.generate(curve='ed448') - self.assertIn("curve='Ed448'", repr(key)) - self.assertEqual(key.curve, 'Ed448') - self.assertEqual(key.public_key().curve, 'Ed448') - - -class TestEccModule_Ed448(unittest.TestCase): - - def test_generate(self): - key = ECC.generate(curve="Ed448") - self.assertTrue(key.has_private()) - point = EccPoint(_curves['Ed448'].Gx, _curves['Ed448'].Gy, curve="Ed448") * key.d - self.assertEqual(key.pointQ, point) - - # Always random - key2 = ECC.generate(curve="Ed448") - self.assertNotEqual(key, key2) - - # Other names - ECC.generate(curve="Ed448") - - # Random source - key1 = ECC.generate(curve="Ed448", randfunc=SHAKE128.new().read) - key2 = ECC.generate(curve="Ed448", randfunc=SHAKE128.new().read) - self.assertEqual(key1, key2) - - def test_construct(self): - seed = unhexlify("4adf5d37ac6785e83e99a924f92676d366a78690af59c92b6bdf14f9cdbcf26fdad478109607583d633b60078d61d51d81b7509c5433b0d4c9") - Px = 0x72a01eea003a35f9ac44231dc4aae2a382f351d80bf32508175b0855edcf389aa2bbf308dd961ce361a6e7c2091bc78957f6ebcf3002a617 - Py = 0x9e0d08d84586e9aeefecacb41d049b831f1a3ee0c3eada63e34557b30702b50ab59fb372feff7c30b8cbb7dd51afbe88444ec56238722ec1 - d = 0xb07cf179604f83433186e5178760c759c15125ee54ff6f8dcde46e872b709ac82ed0bd0a4e036d774034dcb18a9fb11894657a1485895f80 - point = EccPoint(Px, Py, curve="Ed448") - - # Private key only - key = ECC.construct(curve="Ed448", seed=seed) - self.assertEqual(key.pointQ, point) - self.assertTrue(key.has_private()) - - # Public key only - key = ECC.construct(curve="Ed448", point_x=Px, point_y=Py) - self.assertEqual(key.pointQ, point) - self.assertFalse(key.has_private()) - - # Private and public key - key = ECC.construct(curve="Ed448", seed=seed, point_x=Px, point_y=Py) - self.assertEqual(key.pointQ, point) - self.assertTrue(key.has_private()) - - # Other names - key = ECC.construct(curve="ed448", seed=seed) - - def test_negative_construct(self): - coord = dict(point_x=10, point_y=4) - coordG = dict(point_x=_curves['ed448'].Gx, point_y=_curves['ed448'].Gy) - - self.assertRaises(ValueError, ECC.construct, curve="Ed448", **coord) - self.assertRaises(ValueError, ECC.construct, curve="Ed448", d=2, **coordG) - self.assertRaises(ValueError, ECC.construct, curve="Ed448", seed=b'H'*58) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(TestEccPoint_Ed448) - tests += list_test_cases(TestEccKey_Ed448) - tests += list_test_cases(TestEccModule_Ed448) - return tests - - -if __name__ == '__main__': - def suite(): - return unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_ECC_NIST.py b/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_ECC_NIST.py deleted file mode 100644 index cadbd12..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_ECC_NIST.py +++ /dev/null @@ -1,1425 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2015, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import unittest -from binascii import unhexlify - -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.SelfTest.loader import load_test_vectors - -from Cryptodome.PublicKey import ECC -from Cryptodome.PublicKey.ECC import EccPoint, _curves, EccKey - -from Cryptodome.Math.Numbers import Integer - - -class TestEccPoint(unittest.TestCase): - - def test_mix(self): - - p1 = ECC.generate(curve='P-256').pointQ - p2 = ECC.generate(curve='P-384').pointQ - - try: - p1 + p2 - assert(False) - except ValueError as e: - assert "not on the same curve" in str(e) - - try: - p1 += p2 - assert(False) - except ValueError as e: - assert "not on the same curve" in str(e) - - class OtherKeyType: - pass - - self.assertFalse(p1 == OtherKeyType()) - self.assertTrue(p1 != OtherKeyType()) - - def test_repr(self): - p1 = ECC.construct(curve='P-256', - d=75467964919405407085864614198393977741148485328036093939970922195112333446269, - point_x=20573031766139722500939782666697015100983491952082159880539639074939225934381, - point_y=108863130203210779921520632367477406025152638284581252625277850513266505911389) - self.assertEqual(repr(p1), "EccKey(curve='NIST P-256', point_x=20573031766139722500939782666697015100983491952082159880539639074939225934381, point_y=108863130203210779921520632367477406025152638284581252625277850513266505911389, d=75467964919405407085864614198393977741148485328036093939970922195112333446269)") - - -class TestEccPoint_NIST_P192(unittest.TestCase): - """Tests defined in section 4.1 of https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.204.9073&rep=rep1&type=pdf""" - - pointS = EccPoint( - 0xd458e7d127ae671b0c330266d246769353a012073e97acf8, - 0x325930500d851f336bddc050cf7fb11b5673a1645086df3b, - curve='p192') - - pointT = EccPoint( - 0xf22c4395213e9ebe67ddecdd87fdbd01be16fb059b9753a4, - 0x264424096af2b3597796db48f8dfb41fa9cecc97691a9c79, - curve='p192') - - def test_set(self): - pointW = EccPoint(0, 0) - pointW.set(self.pointS) - self.assertEqual(pointW, self.pointS) - - def test_copy(self): - pointW = self.pointS.copy() - self.assertEqual(pointW, self.pointS) - pointW.set(self.pointT) - self.assertEqual(pointW, self.pointT) - self.assertNotEqual(self.pointS, self.pointT) - - def test_negate(self): - negS = -self.pointS - sum = self.pointS + negS - self.assertEqual(sum, self.pointS.point_at_infinity()) - - def test_addition(self): - pointRx = 0x48e1e4096b9b8e5ca9d0f1f077b8abf58e843894de4d0290 - pointRy = 0x408fa77c797cd7dbfb16aa48a3648d3d63c94117d7b6aa4b - - pointR = self.pointS + self.pointT - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - pai = pointR.point_at_infinity() - - # S + 0 - pointR = self.pointS + pai - self.assertEqual(pointR, self.pointS) - - # 0 + S - pointR = pai + self.pointS - self.assertEqual(pointR, self.pointS) - - # 0 + 0 - pointR = pai + pai - self.assertEqual(pointR, pai) - - def test_inplace_addition(self): - pointRx = 0x48e1e4096b9b8e5ca9d0f1f077b8abf58e843894de4d0290 - pointRy = 0x408fa77c797cd7dbfb16aa48a3648d3d63c94117d7b6aa4b - - pointR = self.pointS.copy() - pointR += self.pointT - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - pai = pointR.point_at_infinity() - - # S + 0 - pointR = self.pointS.copy() - pointR += pai - self.assertEqual(pointR, self.pointS) - - # 0 + S - pointR = pai.copy() - pointR += self.pointS - self.assertEqual(pointR, self.pointS) - - # 0 + 0 - pointR = pai.copy() - pointR += pai - self.assertEqual(pointR, pai) - - def test_doubling(self): - pointRx = 0x30c5bc6b8c7da25354b373dc14dd8a0eba42d25a3f6e6962 - pointRy = 0x0dde14bc4249a721c407aedbf011e2ddbbcb2968c9d889cf - - pointR = self.pointS.copy() - pointR.double() - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - # 2*0 - pai = self.pointS.point_at_infinity() - pointR = pai.copy() - pointR.double() - self.assertEqual(pointR, pai) - - # S + S - pointR = self.pointS.copy() - pointR += pointR - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - def test_scalar_multiply(self): - d = 0xa78a236d60baec0c5dd41b33a542463a8255391af64c74ee - pointRx = 0x1faee4205a4f669d2d0a8f25e3bcec9a62a6952965bf6d31 - pointRy = 0x5ff2cdfa508a2581892367087c696f179e7a4d7e8260fb06 - - pointR = self.pointS * d - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - # 0*S - pai = self.pointS.point_at_infinity() - pointR = self.pointS * 0 - self.assertEqual(pointR, pai) - - # -1*S - self.assertRaises(ValueError, lambda: self.pointS * -1) - - # Reverse order - pointR = d * self.pointS - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - pointR = Integer(d) * self.pointS - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - def test_joint_scalar_multiply(self): - d = 0xa78a236d60baec0c5dd41b33a542463a8255391af64c74ee - e = 0xc4be3d53ec3089e71e4de8ceab7cce889bc393cd85b972bc - pointRx = 0x019f64eed8fa9b72b7dfea82c17c9bfa60ecb9e1778b5bde - pointRy = 0x16590c5fcd8655fa4ced33fb800e2a7e3c61f35d83503644 - - pointR = self.pointS * d + self.pointT * e - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - def test_sizes(self): - self.assertEqual(self.pointS.size_in_bits(), 192) - self.assertEqual(self.pointS.size_in_bytes(), 24) - - -class TestEccPoint_NIST_P224(unittest.TestCase): - """Tests defined in section 4.2 of https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.204.9073&rep=rep1&type=pdf""" - - pointS = EccPoint( - 0x6eca814ba59a930843dc814edd6c97da95518df3c6fdf16e9a10bb5b, - 0xef4b497f0963bc8b6aec0ca0f259b89cd80994147e05dc6b64d7bf22, - curve='p224') - - pointT = EccPoint( - 0xb72b25aea5cb03fb88d7e842002969648e6ef23c5d39ac903826bd6d, - 0xc42a8a4d34984f0b71b5b4091af7dceb33ea729c1a2dc8b434f10c34, - curve='p224') - - def test_set(self): - pointW = EccPoint(0, 0) - pointW.set(self.pointS) - self.assertEqual(pointW, self.pointS) - - def test_copy(self): - pointW = self.pointS.copy() - self.assertEqual(pointW, self.pointS) - pointW.set(self.pointT) - self.assertEqual(pointW, self.pointT) - self.assertNotEqual(self.pointS, self.pointT) - - def test_negate(self): - negS = -self.pointS - sum = self.pointS + negS - self.assertEqual(sum, self.pointS.point_at_infinity()) - - def test_addition(self): - pointRx = 0x236f26d9e84c2f7d776b107bd478ee0a6d2bcfcaa2162afae8d2fd15 - pointRy = 0xe53cc0a7904ce6c3746f6a97471297a0b7d5cdf8d536ae25bb0fda70 - - pointR = self.pointS + self.pointT - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - pai = pointR.point_at_infinity() - - # S + 0 - pointR = self.pointS + pai - self.assertEqual(pointR, self.pointS) - - # 0 + S - pointR = pai + self.pointS - self.assertEqual(pointR, self.pointS) - - # 0 + 0 - pointR = pai + pai - self.assertEqual(pointR, pai) - - def test_inplace_addition(self): - pointRx = 0x236f26d9e84c2f7d776b107bd478ee0a6d2bcfcaa2162afae8d2fd15 - pointRy = 0xe53cc0a7904ce6c3746f6a97471297a0b7d5cdf8d536ae25bb0fda70 - - pointR = self.pointS.copy() - pointR += self.pointT - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - pai = pointR.point_at_infinity() - - # S + 0 - pointR = self.pointS.copy() - pointR += pai - self.assertEqual(pointR, self.pointS) - - # 0 + S - pointR = pai.copy() - pointR += self.pointS - self.assertEqual(pointR, self.pointS) - - # 0 + 0 - pointR = pai.copy() - pointR += pai - self.assertEqual(pointR, pai) - - def test_doubling(self): - pointRx = 0xa9c96f2117dee0f27ca56850ebb46efad8ee26852f165e29cb5cdfc7 - pointRy = 0xadf18c84cf77ced4d76d4930417d9579207840bf49bfbf5837dfdd7d - - pointR = self.pointS.copy() - pointR.double() - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - # 2*0 - pai = self.pointS.point_at_infinity() - pointR = pai.copy() - pointR.double() - self.assertEqual(pointR, pai) - - # S + S - pointR = self.pointS.copy() - pointR += pointR - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - def test_scalar_multiply(self): - d = 0xa78ccc30eaca0fcc8e36b2dd6fbb03df06d37f52711e6363aaf1d73b - pointRx = 0x96a7625e92a8d72bff1113abdb95777e736a14c6fdaacc392702bca4 - pointRy = 0x0f8e5702942a3c5e13cd2fd5801915258b43dfadc70d15dbada3ed10 - - pointR = self.pointS * d - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - # 0*S - pai = self.pointS.point_at_infinity() - pointR = self.pointS * 0 - self.assertEqual(pointR, pai) - - # -1*S - self.assertRaises(ValueError, lambda: self.pointS * -1) - - # Reverse order - pointR = d * self.pointS - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - pointR = Integer(d) * self.pointS - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - def test_joing_scalar_multiply(self): - d = 0xa78ccc30eaca0fcc8e36b2dd6fbb03df06d37f52711e6363aaf1d73b - e = 0x54d549ffc08c96592519d73e71e8e0703fc8177fa88aa77a6ed35736 - pointRx = 0xdbfe2958c7b2cda1302a67ea3ffd94c918c5b350ab838d52e288c83e - pointRy = 0x2f521b83ac3b0549ff4895abcc7f0c5a861aacb87acbc5b8147bb18b - - pointR = self.pointS * d + self.pointT * e - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - def test_sizes(self): - self.assertEqual(self.pointS.size_in_bits(), 224) - self.assertEqual(self.pointS.size_in_bytes(), 28) - - -class TestEccPoint_NIST_P256(unittest.TestCase): - """Tests defined in section 4.3 of https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.204.9073&rep=rep1&type=pdf""" - - pointS = EccPoint( - 0xde2444bebc8d36e682edd27e0f271508617519b3221a8fa0b77cab3989da97c9, - 0xc093ae7ff36e5380fc01a5aad1e66659702de80f53cec576b6350b243042a256) - - pointT = EccPoint( - 0x55a8b00f8da1d44e62f6b3b25316212e39540dc861c89575bb8cf92e35e0986b, - 0x5421c3209c2d6c704835d82ac4c3dd90f61a8a52598b9e7ab656e9d8c8b24316) - - def test_set(self): - pointW = EccPoint(0, 0) - pointW.set(self.pointS) - self.assertEqual(pointW, self.pointS) - - def test_copy(self): - pointW = self.pointS.copy() - self.assertEqual(pointW, self.pointS) - pointW.set(self.pointT) - self.assertEqual(pointW, self.pointT) - self.assertNotEqual(self.pointS, self.pointT) - - def test_negate(self): - negS = -self.pointS - sum = self.pointS + negS - self.assertEqual(sum, self.pointS.point_at_infinity()) - - def test_addition(self): - pointRx = 0x72b13dd4354b6b81745195e98cc5ba6970349191ac476bd4553cf35a545a067e - pointRy = 0x8d585cbb2e1327d75241a8a122d7620dc33b13315aa5c9d46d013011744ac264 - - pointR = self.pointS + self.pointT - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - pai = pointR.point_at_infinity() - - # S + 0 - pointR = self.pointS + pai - self.assertEqual(pointR, self.pointS) - - # 0 + S - pointR = pai + self.pointS - self.assertEqual(pointR, self.pointS) - - # 0 + 0 - pointR = pai + pai - self.assertEqual(pointR, pai) - - def test_inplace_addition(self): - pointRx = 0x72b13dd4354b6b81745195e98cc5ba6970349191ac476bd4553cf35a545a067e - pointRy = 0x8d585cbb2e1327d75241a8a122d7620dc33b13315aa5c9d46d013011744ac264 - - pointR = self.pointS.copy() - pointR += self.pointT - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - pai = pointR.point_at_infinity() - - # S + 0 - pointR = self.pointS.copy() - pointR += pai - self.assertEqual(pointR, self.pointS) - - # 0 + S - pointR = pai.copy() - pointR += self.pointS - self.assertEqual(pointR, self.pointS) - - # 0 + 0 - pointR = pai.copy() - pointR += pai - self.assertEqual(pointR, pai) - - def test_doubling(self): - pointRx = 0x7669e6901606ee3ba1a8eef1e0024c33df6c22f3b17481b82a860ffcdb6127b0 - pointRy = 0xfa878162187a54f6c39f6ee0072f33de389ef3eecd03023de10ca2c1db61d0c7 - - pointR = self.pointS.copy() - pointR.double() - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - # 2*0 - pai = self.pointS.point_at_infinity() - pointR = pai.copy() - pointR.double() - self.assertEqual(pointR, pai) - - # S + S - pointR = self.pointS.copy() - pointR += pointR - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - def test_scalar_multiply(self): - d = 0xc51e4753afdec1e6b6c6a5b992f43f8dd0c7a8933072708b6522468b2ffb06fd - pointRx = 0x51d08d5f2d4278882946d88d83c97d11e62becc3cfc18bedacc89ba34eeca03f - pointRy = 0x75ee68eb8bf626aa5b673ab51f6e744e06f8fcf8a6c0cf3035beca956a7b41d5 - - pointR = self.pointS * d - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - # 0*S - pai = self.pointS.point_at_infinity() - pointR = self.pointS * 0 - self.assertEqual(pointR, pai) - - # -1*S - self.assertRaises(ValueError, lambda: self.pointS * -1) - - # Reverse order - pointR = d * self.pointS - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - pointR = Integer(d) * self.pointS - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - def test_joing_scalar_multiply(self): - d = 0xc51e4753afdec1e6b6c6a5b992f43f8dd0c7a8933072708b6522468b2ffb06fd - e = 0xd37f628ece72a462f0145cbefe3f0b355ee8332d37acdd83a358016aea029db7 - pointRx = 0xd867b4679221009234939221b8046245efcf58413daacbeff857b8588341f6b8 - pointRy = 0xf2504055c03cede12d22720dad69c745106b6607ec7e50dd35d54bd80f615275 - - pointR = self.pointS * d + self.pointT * e - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - def test_sizes(self): - self.assertEqual(self.pointS.size_in_bits(), 256) - self.assertEqual(self.pointS.size_in_bytes(), 32) - - -class TestEccPoint_NIST_P384(unittest.TestCase): - """Tests defined in section 4.4 of https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.204.9073&rep=rep1&type=pdf""" - - pointS = EccPoint( - 0xfba203b81bbd23f2b3be971cc23997e1ae4d89e69cb6f92385dda82768ada415ebab4167459da98e62b1332d1e73cb0e, - 0x5ffedbaefdeba603e7923e06cdb5d0c65b22301429293376d5c6944e3fa6259f162b4788de6987fd59aed5e4b5285e45, - "p384") - - pointT = EccPoint( - 0xaacc05202e7fda6fc73d82f0a66220527da8117ee8f8330ead7d20ee6f255f582d8bd38c5a7f2b40bcdb68ba13d81051, - 0x84009a263fefba7c2c57cffa5db3634d286131afc0fca8d25afa22a7b5dce0d9470da89233cee178592f49b6fecb5092, - "p384") - - def test_set(self): - pointW = EccPoint(0, 0, "p384") - pointW.set(self.pointS) - self.assertEqual(pointW, self.pointS) - - def test_copy(self): - pointW = self.pointS.copy() - self.assertEqual(pointW, self.pointS) - pointW.set(self.pointT) - self.assertEqual(pointW, self.pointT) - self.assertNotEqual(self.pointS, self.pointT) - - def test_negate(self): - negS = -self.pointS - sum = self.pointS + negS - self.assertEqual(sum, self.pointS.point_at_infinity()) - - def test_addition(self): - pointRx = 0x12dc5ce7acdfc5844d939f40b4df012e68f865b89c3213ba97090a247a2fc009075cf471cd2e85c489979b65ee0b5eed - pointRy = 0x167312e58fe0c0afa248f2854e3cddcb557f983b3189b67f21eee01341e7e9fe67f6ee81b36988efa406945c8804a4b0 - - pointR = self.pointS + self.pointT - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - pai = pointR.point_at_infinity() - - # S + 0 - pointR = self.pointS + pai - self.assertEqual(pointR, self.pointS) - - # 0 + S - pointR = pai + self.pointS - self.assertEqual(pointR, self.pointS) - - # 0 + 0 - pointR = pai + pai - self.assertEqual(pointR, pai) - - def _test_inplace_addition(self): - pointRx = 0x72b13dd4354b6b81745195e98cc5ba6970349191ac476bd4553cf35a545a067e - pointRy = 0x8d585cbb2e1327d75241a8a122d7620dc33b13315aa5c9d46d013011744ac264 - - pointR = self.pointS.copy() - pointR += self.pointT - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - pai = pointR.point_at_infinity() - - # S + 0 - pointR = self.pointS.copy() - pointR += pai - self.assertEqual(pointR, self.pointS) - - # 0 + S - pointR = pai.copy() - pointR += self.pointS - self.assertEqual(pointR, self.pointS) - - # 0 + 0 - pointR = pai.copy() - pointR += pai - self.assertEqual(pointR, pai) - - def test_doubling(self): - pointRx = 0x2a2111b1e0aa8b2fc5a1975516bc4d58017ff96b25e1bdff3c229d5fac3bacc319dcbec29f9478f42dee597b4641504c - pointRy = 0xfa2e3d9dc84db8954ce8085ef28d7184fddfd1344b4d4797343af9b5f9d837520b450f726443e4114bd4e5bdb2f65ddd - - pointR = self.pointS.copy() - pointR.double() - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - # 2*0 - pai = self.pointS.point_at_infinity() - pointR = pai.copy() - pointR.double() - self.assertEqual(pointR, pai) - - # S + S - pointR = self.pointS.copy() - pointR += pointR - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - def test_scalar_multiply(self): - d = 0xa4ebcae5a665983493ab3e626085a24c104311a761b5a8fdac052ed1f111a5c44f76f45659d2d111a61b5fdd97583480 - pointRx = 0xe4f77e7ffeb7f0958910e3a680d677a477191df166160ff7ef6bb5261f791aa7b45e3e653d151b95dad3d93ca0290ef2 - pointRy = 0xac7dee41d8c5f4a7d5836960a773cfc1376289d3373f8cf7417b0c6207ac32e913856612fc9ff2e357eb2ee05cf9667f - - pointR = self.pointS * d - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - # 0*S - pai = self.pointS.point_at_infinity() - pointR = self.pointS * 0 - self.assertEqual(pointR, pai) - - # -1*S - self.assertRaises(ValueError, lambda: self.pointS * -1) - - def test_joing_scalar_multiply(self): - d = 0xa4ebcae5a665983493ab3e626085a24c104311a761b5a8fdac052ed1f111a5c44f76f45659d2d111a61b5fdd97583480 - e = 0xafcf88119a3a76c87acbd6008e1349b29f4ba9aa0e12ce89bcfcae2180b38d81ab8cf15095301a182afbc6893e75385d - pointRx = 0x917ea28bcd641741ae5d18c2f1bd917ba68d34f0f0577387dc81260462aea60e2417b8bdc5d954fc729d211db23a02dc - pointRy = 0x1a29f7ce6d074654d77b40888c73e92546c8f16a5ff6bcbd307f758d4aee684beff26f6742f597e2585c86da908f7186 - - pointR = self.pointS * d + self.pointT * e - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - def test_sizes(self): - self.assertEqual(self.pointS.size_in_bits(), 384) - self.assertEqual(self.pointS.size_in_bytes(), 48) - - -class TestEccPoint_NIST_P521(unittest.TestCase): - """Tests defined in section 4.5 of https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.204.9073&rep=rep1&type=pdf""" - - pointS = EccPoint( - 0x000001d5c693f66c08ed03ad0f031f937443458f601fd098d3d0227b4bf62873af50740b0bb84aa157fc847bcf8dc16a8b2b8bfd8e2d0a7d39af04b089930ef6dad5c1b4, - 0x00000144b7770963c63a39248865ff36b074151eac33549b224af5c8664c54012b818ed037b2b7c1a63ac89ebaa11e07db89fcee5b556e49764ee3fa66ea7ae61ac01823, - "p521") - - pointT = EccPoint( - 0x000000f411f2ac2eb971a267b80297ba67c322dba4bb21cec8b70073bf88fc1ca5fde3ba09e5df6d39acb2c0762c03d7bc224a3e197feaf760d6324006fe3be9a548c7d5, - 0x000001fdf842769c707c93c630df6d02eff399a06f1b36fb9684f0b373ed064889629abb92b1ae328fdb45534268384943f0e9222afe03259b32274d35d1b9584c65e305, - "p521") - - def test_set(self): - pointW = EccPoint(0, 0) - pointW.set(self.pointS) - self.assertEqual(pointW, self.pointS) - - def test_copy(self): - pointW = self.pointS.copy() - self.assertEqual(pointW, self.pointS) - pointW.set(self.pointT) - self.assertEqual(pointW, self.pointT) - self.assertNotEqual(self.pointS, self.pointT) - - def test_negate(self): - negS = -self.pointS - sum = self.pointS + negS - self.assertEqual(sum, self.pointS.point_at_infinity()) - - def test_addition(self): - pointRx = 0x000001264ae115ba9cbc2ee56e6f0059e24b52c8046321602c59a339cfb757c89a59c358a9a8e1f86d384b3f3b255ea3f73670c6dc9f45d46b6a196dc37bbe0f6b2dd9e9 - pointRy = 0x00000062a9c72b8f9f88a271690bfa017a6466c31b9cadc2fc544744aeb817072349cfddc5ad0e81b03f1897bd9c8c6efbdf68237dc3bb00445979fb373b20c9a967ac55 - - pointR = self.pointS + self.pointT - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - pai = pointR.point_at_infinity() - - # S + 0 - pointR = self.pointS + pai - self.assertEqual(pointR, self.pointS) - - # 0 + S - pointR = pai + self.pointS - self.assertEqual(pointR, self.pointS) - - # 0 + 0 - pointR = pai + pai - self.assertEqual(pointR, pai) - - def test_inplace_addition(self): - pointRx = 0x000001264ae115ba9cbc2ee56e6f0059e24b52c8046321602c59a339cfb757c89a59c358a9a8e1f86d384b3f3b255ea3f73670c6dc9f45d46b6a196dc37bbe0f6b2dd9e9 - pointRy = 0x00000062a9c72b8f9f88a271690bfa017a6466c31b9cadc2fc544744aeb817072349cfddc5ad0e81b03f1897bd9c8c6efbdf68237dc3bb00445979fb373b20c9a967ac55 - - pointR = self.pointS.copy() - pointR += self.pointT - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - pai = pointR.point_at_infinity() - - # S + 0 - pointR = self.pointS.copy() - pointR += pai - self.assertEqual(pointR, self.pointS) - - # 0 + S - pointR = pai.copy() - pointR += self.pointS - self.assertEqual(pointR, self.pointS) - - # 0 + 0 - pointR = pai.copy() - pointR += pai - self.assertEqual(pointR, pai) - - def test_doubling(self): - pointRx = 0x0000012879442f2450c119e7119a5f738be1f1eba9e9d7c6cf41b325d9ce6d643106e9d61124a91a96bcf201305a9dee55fa79136dc700831e54c3ca4ff2646bd3c36bc6 - pointRy = 0x0000019864a8b8855c2479cbefe375ae553e2393271ed36fadfc4494fc0583f6bd03598896f39854abeae5f9a6515a021e2c0eef139e71de610143f53382f4104dccb543 - - pointR = self.pointS.copy() - pointR.double() - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - # 2*0 - pai = self.pointS.point_at_infinity() - pointR = pai.copy() - pointR.double() - self.assertEqual(pointR, pai) - - # S + S - pointR = self.pointS.copy() - pointR += pointR - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - def test_scalar_multiply(self): - d = 0x000001eb7f81785c9629f136a7e8f8c674957109735554111a2a866fa5a166699419bfa9936c78b62653964df0d6da940a695c7294d41b2d6600de6dfcf0edcfc89fdcb1 - pointRx = 0x00000091b15d09d0ca0353f8f96b93cdb13497b0a4bb582ae9ebefa35eee61bf7b7d041b8ec34c6c00c0c0671c4ae063318fb75be87af4fe859608c95f0ab4774f8c95bb - pointRy = 0x00000130f8f8b5e1abb4dd94f6baaf654a2d5810411e77b7423965e0c7fd79ec1ae563c207bd255ee9828eb7a03fed565240d2cc80ddd2cecbb2eb50f0951f75ad87977f - - pointR = self.pointS * d - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - # 0*S - pai = self.pointS.point_at_infinity() - pointR = self.pointS * 0 - self.assertEqual(pointR, pai) - - # -1*S - self.assertRaises(ValueError, lambda: self.pointS * -1) - - def test_joing_scalar_multiply(self): - d = 0x000001eb7f81785c9629f136a7e8f8c674957109735554111a2a866fa5a166699419bfa9936c78b62653964df0d6da940a695c7294d41b2d6600de6dfcf0edcfc89fdcb1 - e = 0x00000137e6b73d38f153c3a7575615812608f2bab3229c92e21c0d1c83cfad9261dbb17bb77a63682000031b9122c2f0cdab2af72314be95254de4291a8f85f7c70412e3 - pointRx = 0x0000009d3802642b3bea152beb9e05fba247790f7fc168072d363340133402f2585588dc1385d40ebcb8552f8db02b23d687cae46185b27528adb1bf9729716e4eba653d - pointRy = 0x0000000fe44344e79da6f49d87c1063744e5957d9ac0a505bafa8281c9ce9ff25ad53f8da084a2deb0923e46501de5797850c61b229023dd9cf7fc7f04cd35ebb026d89d - - pointR = self.pointS * d - pointR += self.pointT * e - self.assertEqual(pointR.x, pointRx) - self.assertEqual(pointR.y, pointRy) - - def test_sizes(self): - self.assertEqual(self.pointS.size_in_bits(), 521) - self.assertEqual(self.pointS.size_in_bytes(), 66) - - -class TestEccPoint_PAI_P192(unittest.TestCase): - """Test vectors from http://point-at-infinity.org/ecc/nisttv""" - - curve = _curves['p192'] - pointG = EccPoint(curve.Gx, curve.Gy, "p192") - - -tv_pai = load_test_vectors(("PublicKey", "ECC"), - "point-at-infinity.org-P192.txt", - "P-192 tests from point-at-infinity.org", - {"k": lambda k: int(k), - "x": lambda x: int(x, 16), - "y": lambda y: int(y, 16)}) or [] -for tv in tv_pai: - def new_test(self, scalar=tv.k, x=tv.x, y=tv.y): - result = self.pointG * scalar - self.assertEqual(result.x, x) - self.assertEqual(result.y, y) - setattr(TestEccPoint_PAI_P192, "test_%d" % tv.count, new_test) - - -class TestEccPoint_PAI_P224(unittest.TestCase): - """Test vectors from http://point-at-infinity.org/ecc/nisttv""" - - curve = _curves['p224'] - pointG = EccPoint(curve.Gx, curve.Gy, "p224") - - -tv_pai = load_test_vectors(("PublicKey", "ECC"), - "point-at-infinity.org-P224.txt", - "P-224 tests from point-at-infinity.org", - {"k": lambda k: int(k), - "x": lambda x: int(x, 16), - "y": lambda y: int(y, 16)}) or [] -for tv in tv_pai: - def new_test(self, scalar=tv.k, x=tv.x, y=tv.y): - result = self.pointG * scalar - self.assertEqual(result.x, x) - self.assertEqual(result.y, y) - setattr(TestEccPoint_PAI_P224, "test_%d" % tv.count, new_test) - - -class TestEccPoint_PAI_P256(unittest.TestCase): - """Test vectors from http://point-at-infinity.org/ecc/nisttv""" - - curve = _curves['p256'] - pointG = EccPoint(curve.Gx, curve.Gy, "p256") - - -tv_pai = load_test_vectors(("PublicKey", "ECC"), - "point-at-infinity.org-P256.txt", - "P-256 tests from point-at-infinity.org", - {"k": lambda k: int(k), - "x": lambda x: int(x, 16), - "y": lambda y: int(y, 16)}) or [] -for tv in tv_pai: - def new_test(self, scalar=tv.k, x=tv.x, y=tv.y): - result = self.pointG * scalar - self.assertEqual(result.x, x) - self.assertEqual(result.y, y) - setattr(TestEccPoint_PAI_P256, "test_%d" % tv.count, new_test) - - -class TestEccPoint_PAI_P384(unittest.TestCase): - """Test vectors from http://point-at-infinity.org/ecc/nisttv""" - - curve = _curves['p384'] - pointG = EccPoint(curve.Gx, curve.Gy, "p384") - - -tv_pai = load_test_vectors(("PublicKey", "ECC"), - "point-at-infinity.org-P384.txt", - "P-384 tests from point-at-infinity.org", - {"k": lambda k: int(k), - "x": lambda x: int(x, 16), - "y": lambda y: int(y, 16)}) or [] -for tv in tv_pai: - def new_test(self, scalar=tv.k, x=tv.x, y=tv.y): - result = self.pointG * scalar - self.assertEqual(result.x, x) - self.assertEqual(result.y, y) - setattr(TestEccPoint_PAI_P384, "test_%d" % tv.count, new_test) - - -class TestEccPoint_PAI_P521(unittest.TestCase): - """Test vectors from http://point-at-infinity.org/ecc/nisttv""" - - curve = _curves['p521'] - pointG = EccPoint(curve.Gx, curve.Gy, "p521") - - -tv_pai = load_test_vectors(("PublicKey", "ECC"), - "point-at-infinity.org-P521.txt", - "P-521 tests from point-at-infinity.org", - {"k": lambda k: int(k), - "x": lambda x: int(x, 16), - "y": lambda y: int(y, 16)}) or [] -for tv in tv_pai: - def new_test(self, scalar=tv.k, x=tv.x, y=tv.y): - result = self.pointG * scalar - self.assertEqual(result.x, x) - self.assertEqual(result.y, y) - setattr(TestEccPoint_PAI_P521, "test_%d" % tv.count, new_test) - - -class TestEccKey_P192(unittest.TestCase): - - def test_private_key(self): - - key = EccKey(curve="P-192", d=1) - self.assertEqual(key.d, 1) - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ.x, _curves['p192'].Gx) - self.assertEqual(key.pointQ.y, _curves['p192'].Gy) - - point = EccPoint(_curves['p192'].Gx, _curves['p192'].Gy, curve='P-192') - key = EccKey(curve="P-192", d=1, point=point) - self.assertEqual(key.d, 1) - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ, point) - - # Other names - key = EccKey(curve="secp192r1", d=1) - key = EccKey(curve="prime192v1", d=1) - - def test_public_key(self): - - point = EccPoint(_curves['p192'].Gx, _curves['p192'].Gy, curve='P-192') - key = EccKey(curve="P-192", point=point) - self.assertFalse(key.has_private()) - self.assertEqual(key.pointQ, point) - - def test_public_key_derived(self): - - priv_key = EccKey(curve="P-192", d=3) - pub_key = priv_key.public_key() - self.assertFalse(pub_key.has_private()) - self.assertEqual(priv_key.pointQ, pub_key.pointQ) - - def test_invalid_curve(self): - self.assertRaises(ValueError, lambda: EccKey(curve="P-193", d=1)) - - def test_invalid_d(self): - self.assertRaises(ValueError, lambda: EccKey(curve="P-192", d=0)) - self.assertRaises(ValueError, lambda: EccKey(curve="P-192", - d=_curves['p192'].order)) - - def test_equality(self): - - private_key = ECC.construct(d=3, curve="P-192") - private_key2 = ECC.construct(d=3, curve="P-192") - private_key3 = ECC.construct(d=4, curve="P-192") - - public_key = private_key.public_key() - public_key2 = private_key2.public_key() - public_key3 = private_key3.public_key() - - self.assertEqual(private_key, private_key2) - self.assertNotEqual(private_key, private_key3) - - self.assertEqual(public_key, public_key2) - self.assertNotEqual(public_key, public_key3) - - self.assertNotEqual(public_key, private_key) - - def test_name_consistency(self): - key = ECC.generate(curve='p192') - self.assertIn("curve='NIST P-192'", repr(key)) - self.assertEqual(key.curve, 'NIST P-192') - self.assertEqual(key.public_key().curve, 'NIST P-192') - - -class TestEccKey_P224(unittest.TestCase): - - def test_private_key(self): - - key = EccKey(curve="P-224", d=1) - self.assertEqual(key.d, 1) - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ.x, _curves['p224'].Gx) - self.assertEqual(key.pointQ.y, _curves['p224'].Gy) - - point = EccPoint(_curves['p224'].Gx, _curves['p224'].Gy, curve='P-224') - key = EccKey(curve="P-224", d=1, point=point) - self.assertEqual(key.d, 1) - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ, point) - - # Other names - key = EccKey(curve="secp224r1", d=1) - key = EccKey(curve="prime224v1", d=1) - - def test_public_key(self): - - point = EccPoint(_curves['p224'].Gx, _curves['p224'].Gy, curve='P-224') - key = EccKey(curve="P-224", point=point) - self.assertFalse(key.has_private()) - self.assertEqual(key.pointQ, point) - - def test_public_key_derived(self): - - priv_key = EccKey(curve="P-224", d=3) - pub_key = priv_key.public_key() - self.assertFalse(pub_key.has_private()) - self.assertEqual(priv_key.pointQ, pub_key.pointQ) - - def test_invalid_curve(self): - self.assertRaises(ValueError, lambda: EccKey(curve="P-225", d=1)) - - def test_invalid_d(self): - self.assertRaises(ValueError, lambda: EccKey(curve="P-224", d=0)) - self.assertRaises(ValueError, lambda: EccKey(curve="P-224", - d=_curves['p224'].order)) - - def test_equality(self): - - private_key = ECC.construct(d=3, curve="P-224") - private_key2 = ECC.construct(d=3, curve="P-224") - private_key3 = ECC.construct(d=4, curve="P-224") - - public_key = private_key.public_key() - public_key2 = private_key2.public_key() - public_key3 = private_key3.public_key() - - self.assertEqual(private_key, private_key2) - self.assertNotEqual(private_key, private_key3) - - self.assertEqual(public_key, public_key2) - self.assertNotEqual(public_key, public_key3) - - self.assertNotEqual(public_key, private_key) - - def test_name_consistency(self): - key = ECC.generate(curve='p224') - self.assertIn("curve='NIST P-224'", repr(key)) - self.assertEqual(key.curve, 'NIST P-224') - self.assertEqual(key.public_key().curve, 'NIST P-224') - - -class TestEccKey_P256(unittest.TestCase): - - def test_private_key(self): - - key = EccKey(curve="P-256", d=1) - self.assertEqual(key.d, 1) - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ.x, _curves['p256'].Gx) - self.assertEqual(key.pointQ.y, _curves['p256'].Gy) - - point = EccPoint(_curves['p256'].Gx, _curves['p256'].Gy) - key = EccKey(curve="P-256", d=1, point=point) - self.assertEqual(key.d, 1) - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ, point) - - # Other names - key = EccKey(curve="secp256r1", d=1) - key = EccKey(curve="prime256v1", d=1) - - # Must not accept d parameter - self.assertRaises(ValueError, EccKey, curve="p256", seed=b'H'*32) - - def test_public_key(self): - - point = EccPoint(_curves['p256'].Gx, _curves['p256'].Gy) - key = EccKey(curve="P-256", point=point) - self.assertFalse(key.has_private()) - self.assertEqual(key.pointQ, point) - - def test_public_key_derived(self): - - priv_key = EccKey(curve="P-256", d=3) - pub_key = priv_key.public_key() - self.assertFalse(pub_key.has_private()) - self.assertEqual(priv_key.pointQ, pub_key.pointQ) - - def test_invalid_curve(self): - self.assertRaises(ValueError, lambda: EccKey(curve="P-257", d=1)) - - def test_invalid_d(self): - self.assertRaises(ValueError, lambda: EccKey(curve="P-256", d=0)) - self.assertRaises(ValueError, lambda: EccKey(curve="P-256", d=_curves['p256'].order)) - - def test_equality(self): - - private_key = ECC.construct(d=3, curve="P-256") - private_key2 = ECC.construct(d=3, curve="P-256") - private_key3 = ECC.construct(d=4, curve="P-256") - - public_key = private_key.public_key() - public_key2 = private_key2.public_key() - public_key3 = private_key3.public_key() - - self.assertEqual(private_key, private_key2) - self.assertNotEqual(private_key, private_key3) - - self.assertEqual(public_key, public_key2) - self.assertNotEqual(public_key, public_key3) - - self.assertNotEqual(public_key, private_key) - - def test_name_consistency(self): - key = ECC.generate(curve='p256') - self.assertIn("curve='NIST P-256'", repr(key)) - self.assertEqual(key.curve, 'NIST P-256') - self.assertEqual(key.public_key().curve, 'NIST P-256') - - -class TestEccKey_P384(unittest.TestCase): - - def test_private_key(self): - - p384 = _curves['p384'] - - key = EccKey(curve="P-384", d=1) - self.assertEqual(key.d, 1) - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ.x, p384.Gx) - self.assertEqual(key.pointQ.y, p384.Gy) - - point = EccPoint(p384.Gx, p384.Gy, "p384") - key = EccKey(curve="P-384", d=1, point=point) - self.assertEqual(key.d, 1) - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ, point) - - # Other names - key = EccKey(curve="p384", d=1) - key = EccKey(curve="secp384r1", d=1) - key = EccKey(curve="prime384v1", d=1) - - def test_public_key(self): - - p384 = _curves['p384'] - point = EccPoint(p384.Gx, p384.Gy, 'p384') - key = EccKey(curve="P-384", point=point) - self.assertFalse(key.has_private()) - self.assertEqual(key.pointQ, point) - - def test_public_key_derived(self): - - priv_key = EccKey(curve="P-384", d=3) - pub_key = priv_key.public_key() - self.assertFalse(pub_key.has_private()) - self.assertEqual(priv_key.pointQ, pub_key.pointQ) - - def test_invalid_curve(self): - self.assertRaises(ValueError, lambda: EccKey(curve="P-385", d=1)) - - def test_invalid_d(self): - self.assertRaises(ValueError, lambda: EccKey(curve="P-384", d=0)) - self.assertRaises(ValueError, lambda: EccKey(curve="P-384", - d=_curves['p384'].order)) - - def test_equality(self): - - private_key = ECC.construct(d=3, curve="P-384") - private_key2 = ECC.construct(d=3, curve="P-384") - private_key3 = ECC.construct(d=4, curve="P-384") - - public_key = private_key.public_key() - public_key2 = private_key2.public_key() - public_key3 = private_key3.public_key() - - self.assertEqual(private_key, private_key2) - self.assertNotEqual(private_key, private_key3) - - self.assertEqual(public_key, public_key2) - self.assertNotEqual(public_key, public_key3) - - self.assertNotEqual(public_key, private_key) - - def test_name_consistency(self): - key = ECC.generate(curve='p384') - self.assertIn("curve='NIST P-384'", repr(key)) - self.assertEqual(key.curve, 'NIST P-384') - self.assertEqual(key.public_key().curve, 'NIST P-384') - - -class TestEccKey_P521(unittest.TestCase): - - def test_private_key(self): - - p521 = _curves['p521'] - - key = EccKey(curve="P-521", d=1) - self.assertEqual(key.d, 1) - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ.x, p521.Gx) - self.assertEqual(key.pointQ.y, p521.Gy) - - point = EccPoint(p521.Gx, p521.Gy, "p521") - key = EccKey(curve="P-521", d=1, point=point) - self.assertEqual(key.d, 1) - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ, point) - - # Other names - key = EccKey(curve="p521", d=1) - key = EccKey(curve="secp521r1", d=1) - key = EccKey(curve="prime521v1", d=1) - - def test_public_key(self): - - p521 = _curves['p521'] - point = EccPoint(p521.Gx, p521.Gy, 'p521') - key = EccKey(curve="P-384", point=point) - self.assertFalse(key.has_private()) - self.assertEqual(key.pointQ, point) - - def test_public_key_derived(self): - - priv_key = EccKey(curve="P-521", d=3) - pub_key = priv_key.public_key() - self.assertFalse(pub_key.has_private()) - self.assertEqual(priv_key.pointQ, pub_key.pointQ) - - def test_invalid_curve(self): - self.assertRaises(ValueError, lambda: EccKey(curve="P-522", d=1)) - - def test_invalid_d(self): - self.assertRaises(ValueError, lambda: EccKey(curve="P-521", d=0)) - self.assertRaises(ValueError, lambda: EccKey(curve="P-521", - d=_curves['p521'].order)) - - def test_equality(self): - - private_key = ECC.construct(d=3, curve="P-521") - private_key2 = ECC.construct(d=3, curve="P-521") - private_key3 = ECC.construct(d=4, curve="P-521") - - public_key = private_key.public_key() - public_key2 = private_key2.public_key() - public_key3 = private_key3.public_key() - - self.assertEqual(private_key, private_key2) - self.assertNotEqual(private_key, private_key3) - - self.assertEqual(public_key, public_key2) - self.assertNotEqual(public_key, public_key3) - - self.assertNotEqual(public_key, private_key) - - def test_name_consistency(self): - key = ECC.generate(curve='p521') - self.assertIn("curve='NIST P-521'", repr(key)) - self.assertEqual(key.curve, 'NIST P-521') - self.assertEqual(key.public_key().curve, 'NIST P-521') - - -class TestEccModule_P192(unittest.TestCase): - - def test_generate(self): - - key = ECC.generate(curve="P-192") - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ, EccPoint(_curves['p192'].Gx, - _curves['p192'].Gy, - "P-192") * key.d, - "p192") - - # Other names - ECC.generate(curve="secp192r1") - ECC.generate(curve="prime192v1") - - def test_construct(self): - - key = ECC.construct(curve="P-192", d=1) - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ, _curves['p192'].G) - - key = ECC.construct(curve="P-192", point_x=_curves['p192'].Gx, - point_y=_curves['p192'].Gy) - self.assertFalse(key.has_private()) - self.assertEqual(key.pointQ, _curves['p192'].G) - - # Other names - ECC.construct(curve="p192", d=1) - ECC.construct(curve="secp192r1", d=1) - ECC.construct(curve="prime192v1", d=1) - - def test_negative_construct(self): - coord = dict(point_x=10, point_y=4) - coordG = dict(point_x=_curves['p192'].Gx, point_y=_curves['p192'].Gy) - - self.assertRaises(ValueError, ECC.construct, curve="P-192", **coord) - self.assertRaises(ValueError, ECC.construct, curve="P-192", d=2, **coordG) - - -class TestEccModule_P224(unittest.TestCase): - - def test_generate(self): - - key = ECC.generate(curve="P-224") - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ, EccPoint(_curves['p224'].Gx, - _curves['p224'].Gy, - "P-224") * key.d, - "p224") - - # Other names - ECC.generate(curve="secp224r1") - ECC.generate(curve="prime224v1") - - def test_construct(self): - - key = ECC.construct(curve="P-224", d=1) - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ, _curves['p224'].G) - - key = ECC.construct(curve="P-224", point_x=_curves['p224'].Gx, - point_y=_curves['p224'].Gy) - self.assertFalse(key.has_private()) - self.assertEqual(key.pointQ, _curves['p224'].G) - - # Other names - ECC.construct(curve="p224", d=1) - ECC.construct(curve="secp224r1", d=1) - ECC.construct(curve="prime224v1", d=1) - - def test_negative_construct(self): - coord = dict(point_x=10, point_y=4) - coordG = dict(point_x=_curves['p224'].Gx, point_y=_curves['p224'].Gy) - - self.assertRaises(ValueError, ECC.construct, curve="P-224", **coord) - self.assertRaises(ValueError, ECC.construct, curve="P-224", d=2, **coordG) - - -class TestEccModule_P256(unittest.TestCase): - - def test_generate(self): - - key = ECC.generate(curve="P-256") - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ, EccPoint(_curves['p256'].Gx, - _curves['p256'].Gy) * key.d, - "p256") - - # Other names - ECC.generate(curve="secp256r1") - ECC.generate(curve="prime256v1") - - def test_construct(self): - - key = ECC.construct(curve="P-256", d=1) - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ, _curves['p256'].G) - - key = ECC.construct(curve="P-256", point_x=_curves['p256'].Gx, - point_y=_curves['p256'].Gy) - self.assertFalse(key.has_private()) - self.assertEqual(key.pointQ, _curves['p256'].G) - - # Other names - ECC.construct(curve="p256", d=1) - ECC.construct(curve="secp256r1", d=1) - ECC.construct(curve="prime256v1", d=1) - - def test_negative_construct(self): - coord = dict(point_x=10, point_y=4) - coordG = dict(point_x=_curves['p256'].Gx, point_y=_curves['p256'].Gy) - - self.assertRaises(ValueError, ECC.construct, curve="P-256", **coord) - self.assertRaises(ValueError, ECC.construct, curve="P-256", d=2, **coordG) - - -class TestEccModule_P384(unittest.TestCase): - - def test_generate(self): - - curve = _curves['p384'] - key = ECC.generate(curve="P-384") - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ, EccPoint(curve.Gx, curve.Gy, "p384") * key.d) - - # Other names - ECC.generate(curve="secp384r1") - ECC.generate(curve="prime384v1") - - def test_construct(self): - - curve = _curves['p384'] - key = ECC.construct(curve="P-384", d=1) - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ, _curves['p384'].G) - - key = ECC.construct(curve="P-384", point_x=curve.Gx, point_y=curve.Gy) - self.assertFalse(key.has_private()) - self.assertEqual(key.pointQ, curve.G) - - # Other names - ECC.construct(curve="p384", d=1) - ECC.construct(curve="secp384r1", d=1) - ECC.construct(curve="prime384v1", d=1) - - def test_negative_construct(self): - coord = dict(point_x=10, point_y=4) - coordG = dict(point_x=_curves['p384'].Gx, point_y=_curves['p384'].Gy) - - self.assertRaises(ValueError, ECC.construct, curve="P-384", **coord) - self.assertRaises(ValueError, ECC.construct, curve="P-384", d=2, **coordG) - - -class TestEccModule_P521(unittest.TestCase): - - def test_generate(self): - - curve = _curves['p521'] - key = ECC.generate(curve="P-521") - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ, EccPoint(curve.Gx, curve.Gy, "p521") * key.d) - - # Other names - ECC.generate(curve="secp521r1") - ECC.generate(curve="prime521v1") - - def test_construct(self): - - curve = _curves['p521'] - key = ECC.construct(curve="P-521", d=1) - self.assertTrue(key.has_private()) - self.assertEqual(key.pointQ, _curves['p521'].G) - - key = ECC.construct(curve="P-521", point_x=curve.Gx, point_y=curve.Gy) - self.assertFalse(key.has_private()) - self.assertEqual(key.pointQ, curve.G) - - # Other names - ECC.construct(curve="p521", d=1) - ECC.construct(curve="secp521r1", d=1) - ECC.construct(curve="prime521v1", d=1) - - def test_negative_construct(self): - coord = dict(point_x=10, point_y=4) - coordG = dict(point_x=_curves['p521'].Gx, point_y=_curves['p521'].Gy) - - self.assertRaises(ValueError, ECC.construct, curve="P-521", **coord) - self.assertRaises(ValueError, ECC.construct, curve="P-521", d=2, **coordG) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(TestEccPoint) - tests += list_test_cases(TestEccPoint_NIST_P192) - tests += list_test_cases(TestEccPoint_NIST_P224) - tests += list_test_cases(TestEccPoint_NIST_P256) - tests += list_test_cases(TestEccPoint_NIST_P384) - tests += list_test_cases(TestEccPoint_NIST_P521) - tests += list_test_cases(TestEccPoint_PAI_P192) - tests += list_test_cases(TestEccPoint_PAI_P224) - tests += list_test_cases(TestEccPoint_PAI_P256) - tests += list_test_cases(TestEccPoint_PAI_P384) - tests += list_test_cases(TestEccPoint_PAI_P521) - tests += list_test_cases(TestEccKey_P192) - tests += list_test_cases(TestEccKey_P224) - tests += list_test_cases(TestEccKey_P256) - tests += list_test_cases(TestEccKey_P384) - tests += list_test_cases(TestEccKey_P521) - tests += list_test_cases(TestEccModule_P192) - tests += list_test_cases(TestEccModule_P224) - tests += list_test_cases(TestEccModule_P256) - tests += list_test_cases(TestEccModule_P384) - tests += list_test_cases(TestEccModule_P521) - return tests - - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_ElGamal.py b/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_ElGamal.py deleted file mode 100644 index 67d2e0b..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_ElGamal.py +++ /dev/null @@ -1,217 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/PublicKey/test_ElGamal.py: Self-test for the ElGamal primitive -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.PublicKey.ElGamal""" - -__revision__ = "$Id$" - -import unittest -from Cryptodome.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex -from Cryptodome import Random -from Cryptodome.PublicKey import ElGamal -from Cryptodome.Util.number import bytes_to_long -from Cryptodome.Util.py3compat import * - -class ElGamalTest(unittest.TestCase): - - # - # Test vectors - # - # There seem to be no real ElGamal test vectors available in the - # public domain. The following test vectors have been generated - # with libgcrypt 1.5.0. - # - # Encryption - tve=[ - { - # 256 bits - 'p' :'BA4CAEAAED8CBE952AFD2126C63EB3B345D65C2A0A73D2A3AD4138B6D09BD933', - 'g' :'05', - 'y' :'60D063600ECED7C7C55146020E7A31C4476E9793BEAED420FEC9E77604CAE4EF', - 'x' :'1D391BA2EE3C37FE1BA175A69B2C73A11238AD77675932', - 'k' :'F5893C5BAB4131264066F57AB3D8AD89E391A0B68A68A1', - 'pt' :'48656C6C6F207468657265', - 'ct1':'32BFD5F487966CEA9E9356715788C491EC515E4ED48B58F0F00971E93AAA5EC7', - 'ct2':'7BE8FBFF317C93E82FCEF9BD515284BA506603FEA25D01C0CB874A31F315EE68' - }, - - { - # 512 bits - 'p' :'F1B18AE9F7B4E08FDA9A04832F4E919D89462FD31BF12F92791A93519F75076D6CE3942689CDFF2F344CAFF0F82D01864F69F3AECF566C774CBACF728B81A227', - 'g' :'07', - 'y' :'688628C676E4F05D630E1BE39D0066178CA7AA83836B645DE5ADD359B4825A12B02EF4252E4E6FA9BEC1DB0BE90F6D7C8629CABB6E531F472B2664868156E20C', - 'x' :'14E60B1BDFD33436C0DA8A22FDC14A2CCDBBED0627CE68', - 'k' :'38DBF14E1F319BDA9BAB33EEEADCAF6B2EA5250577ACE7', - 'pt' :'48656C6C6F207468657265', - 'ct1':'290F8530C2CC312EC46178724F196F308AD4C523CEABB001FACB0506BFED676083FE0F27AC688B5C749AB3CB8A80CD6F7094DBA421FB19442F5A413E06A9772B', - 'ct2':'1D69AAAD1DC50493FB1B8E8721D621D683F3BF1321BE21BC4A43E11B40C9D4D9C80DE3AAC2AB60D31782B16B61112E68220889D53C4C3136EE6F6CE61F8A23A0' - } - ] - - # Signature - tvs=[ - { - # 256 bits - 'p' :'D2F3C41EA66530838A704A48FFAC9334F4701ECE3A97CEE4C69DD01AE7129DD7', - 'g' :'05', - 'y' :'C3F9417DC0DAFEA6A05C1D2333B7A95E63B3F4F28CC962254B3256984D1012E7', - 'x' :'165E4A39BE44D5A2D8B1332D416BC559616F536BC735BB', - 'k' :'C7F0C794A7EAD726E25A47FF8928013680E73C51DD3D7D99BFDA8F492585928F', - 'h' :'48656C6C6F207468657265', - 'sig1':'35CA98133779E2073EF31165AFCDEB764DD54E96ADE851715495F9C635E1E7C2', - 'sig2':'0135B88B1151279FE5D8078D4FC685EE81177EE9802AB123A73925FC1CB059A7', - }, - { - # 512 bits - 'p' :'E24CF3A4B8A6AF749DCA6D714282FE4AABEEE44A53BB6ED15FBE32B5D3C3EF9CC4124A2ECA331F3C1C1B667ACA3766825217E7B5F9856648D95F05330C6A19CF', - 'g' :'0B', - 'y' :'2AD3A1049CA5D4ED207B2431C79A8719BB4073D4A94E450EA6CEE8A760EB07ADB67C0D52C275EE85D7B52789061EE45F2F37D9B2AE522A51C28329766BFE68AC', - 'x' :'16CBB4F46D9ECCF24FF9F7E63CAA3BD8936341555062AB', - 'k' :'8A3D89A4E429FD2476D7D717251FB79BF900FFE77444E6BB8299DC3F84D0DD57ABAB50732AE158EA52F5B9E7D8813E81FD9F79470AE22F8F1CF9AEC820A78C69', - 'h' :'48656C6C6F207468657265', - 'sig1':'BE001AABAFFF976EC9016198FBFEA14CBEF96B000CCC0063D3324016F9E91FE80D8F9325812ED24DDB2B4D4CF4430B169880B3CE88313B53255BD4EC0378586F', - 'sig2':'5E266F3F837BA204E3BBB6DBECC0611429D96F8C7CE8F4EFDF9D4CB681C2A954468A357BF4242CEC7418B51DFC081BCD21299EF5B5A0DDEF3A139A1817503DDE', - } - ] - - def test_generate_180(self): - self._test_random_key(180) - - def test_encryption(self): - for tv in self.tve: - d = self.convert_tv(tv, True) - key = ElGamal.construct(d['key']) - ct = key._encrypt(d['pt'], d['k']) - self.assertEqual(ct[0], d['ct1']) - self.assertEqual(ct[1], d['ct2']) - - def test_decryption(self): - for tv in self.tve: - d = self.convert_tv(tv, True) - key = ElGamal.construct(d['key']) - pt = key._decrypt((d['ct1'], d['ct2'])) - self.assertEqual(pt, d['pt']) - - def test_signing(self): - for tv in self.tvs: - d = self.convert_tv(tv, True) - key = ElGamal.construct(d['key']) - sig1, sig2 = key._sign(d['h'], d['k']) - self.assertEqual(sig1, d['sig1']) - self.assertEqual(sig2, d['sig2']) - - def test_verification(self): - for tv in self.tvs: - d = self.convert_tv(tv, True) - key = ElGamal.construct(d['key']) - # Positive test - res = key._verify( d['h'], (d['sig1'],d['sig2']) ) - self.assertTrue(res) - # Negative test - res = key._verify( d['h'], (d['sig1']+1,d['sig2']) ) - self.assertFalse(res) - - def test_bad_key3(self): - tup = tup0 = list(self.convert_tv(self.tvs[0], 1)['key'])[:3] - tup[0] += 1 # p += 1 (not prime) - self.assertRaises(ValueError, ElGamal.construct, tup) - - tup = tup0 - tup[1] = 1 # g = 1 - self.assertRaises(ValueError, ElGamal.construct, tup) - - tup = tup0 - tup[2] = tup[0]*2 # y = 2*p - self.assertRaises(ValueError, ElGamal.construct, tup) - - def test_bad_key4(self): - tup = tup0 = list(self.convert_tv(self.tvs[0], 1)['key']) - tup[3] += 1 # x += 1 - self.assertRaises(ValueError, ElGamal.construct, tup) - - def convert_tv(self, tv, as_longs=0): - """Convert a test vector from textual form (hexadecimal ascii - to either integers or byte strings.""" - key_comps = 'p','g','y','x' - tv2 = {} - for c in tv.keys(): - tv2[c] = a2b_hex(tv[c]) - if as_longs or c in key_comps or c in ('sig1','sig2'): - tv2[c] = bytes_to_long(tv2[c]) - tv2['key']=[] - for c in key_comps: - tv2['key'] += [tv2[c]] - del tv2[c] - return tv2 - - def _test_random_key(self, bits): - elgObj = ElGamal.generate(bits, Random.new().read) - self._check_private_key(elgObj) - self._exercise_primitive(elgObj) - pub = elgObj.publickey() - self._check_public_key(pub) - self._exercise_public_primitive(elgObj) - - def _check_private_key(self, elgObj): - - # Check capabilities - self.assertTrue(elgObj.has_private()) - - # Sanity check key data - self.assertTrue(1 -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.PublicKey.RSA""" - -__revision__ = "$Id$" - -import os -import pickle -from pickle import PicklingError -from Cryptodome.Util.py3compat import * - -import unittest -from Cryptodome.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex - -class RSATest(unittest.TestCase): - # Test vectors from "RSA-OAEP and RSA-PSS test vectors (.zip file)" - # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip - # See RSADSI's PKCS#1 page at - # http://www.rsa.com/rsalabs/node.asp?id=2125 - - # from oaep-int.txt - - # TODO: PyCryptodome treats the message as starting *after* the leading "00" - # TODO: That behaviour should probably be changed in the future. - plaintext = """ - eb 7a 19 ac e9 e3 00 63 50 e3 29 50 4b 45 e2 - ca 82 31 0b 26 dc d8 7d 5c 68 f1 ee a8 f5 52 67 - c3 1b 2e 8b b4 25 1f 84 d7 e0 b2 c0 46 26 f5 af - f9 3e dc fb 25 c9 c2 b3 ff 8a e1 0e 83 9a 2d db - 4c dc fe 4f f4 77 28 b4 a1 b7 c1 36 2b aa d2 9a - b4 8d 28 69 d5 02 41 21 43 58 11 59 1b e3 92 f9 - 82 fb 3e 87 d0 95 ae b4 04 48 db 97 2f 3a c1 4f - 7b c2 75 19 52 81 ce 32 d2 f1 b7 6d 4d 35 3e 2d - """ - - ciphertext = """ - 12 53 e0 4d c0 a5 39 7b b4 4a 7a b8 7e 9b f2 a0 - 39 a3 3d 1e 99 6f c8 2a 94 cc d3 00 74 c9 5d f7 - 63 72 20 17 06 9e 52 68 da 5d 1c 0b 4f 87 2c f6 - 53 c1 1d f8 23 14 a6 79 68 df ea e2 8d ef 04 bb - 6d 84 b1 c3 1d 65 4a 19 70 e5 78 3b d6 eb 96 a0 - 24 c2 ca 2f 4a 90 fe 9f 2e f5 c9 c1 40 e5 bb 48 - da 95 36 ad 87 00 c8 4f c9 13 0a de a7 4e 55 8d - 51 a7 4d df 85 d8 b5 0d e9 68 38 d6 06 3e 09 55 - """ - - modulus = """ - bb f8 2f 09 06 82 ce 9c 23 38 ac 2b 9d a8 71 f7 - 36 8d 07 ee d4 10 43 a4 40 d6 b6 f0 74 54 f5 1f - b8 df ba af 03 5c 02 ab 61 ea 48 ce eb 6f cd 48 - 76 ed 52 0d 60 e1 ec 46 19 71 9d 8a 5b 8b 80 7f - af b8 e0 a3 df c7 37 72 3e e6 b4 b7 d9 3a 25 84 - ee 6a 64 9d 06 09 53 74 88 34 b2 45 45 98 39 4e - e0 aa b1 2d 7b 61 a5 1f 52 7a 9a 41 f6 c1 68 7f - e2 53 72 98 ca 2a 8f 59 46 f8 e5 fd 09 1d bd cb - """ - - e = 0x11 # public exponent - - prime_factor = """ - c9 7f b1 f0 27 f4 53 f6 34 12 33 ea aa d1 d9 35 - 3f 6c 42 d0 88 66 b1 d0 5a 0f 20 35 02 8b 9d 86 - 98 40 b4 16 66 b4 2e 92 ea 0d a3 b4 32 04 b5 cf - ce 33 52 52 4d 04 16 a5 a4 41 e7 00 af 46 15 03 - """ - - def setUp(self): - global RSA, Random, bytes_to_long - from Cryptodome.PublicKey import RSA - from Cryptodome import Random - from Cryptodome.Util.number import bytes_to_long, inverse - self.n = bytes_to_long(a2b_hex(self.modulus)) - self.p = bytes_to_long(a2b_hex(self.prime_factor)) - - # Compute q, d, and u from n, e, and p - self.q = self.n // self.p - self.d = inverse(self.e, (self.p-1)*(self.q-1)) - self.u = inverse(self.p, self.q) # u = e**-1 (mod q) - - self.rsa = RSA - - def test_generate_1arg(self): - """RSA (default implementation) generated key (1 argument)""" - rsaObj = self.rsa.generate(1024) - self._check_private_key(rsaObj) - self._exercise_primitive(rsaObj) - pub = rsaObj.public_key() - self._check_public_key(pub) - self._exercise_public_primitive(rsaObj) - - def test_generate_2arg(self): - """RSA (default implementation) generated key (2 arguments)""" - rsaObj = self.rsa.generate(1024, Random.new().read) - self._check_private_key(rsaObj) - self._exercise_primitive(rsaObj) - pub = rsaObj.public_key() - self._check_public_key(pub) - self._exercise_public_primitive(rsaObj) - - def test_generate_3args(self): - rsaObj = self.rsa.generate(1024, Random.new().read,e=65537) - self._check_private_key(rsaObj) - self._exercise_primitive(rsaObj) - pub = rsaObj.public_key() - self._check_public_key(pub) - self._exercise_public_primitive(rsaObj) - self.assertEqual(65537,rsaObj.e) - - def test_construct_2tuple(self): - """RSA (default implementation) constructed key (2-tuple)""" - pub = self.rsa.construct((self.n, self.e)) - self._check_public_key(pub) - self._check_encryption(pub) - - def test_construct_3tuple(self): - """RSA (default implementation) constructed key (3-tuple)""" - rsaObj = self.rsa.construct((self.n, self.e, self.d)) - self._check_encryption(rsaObj) - self._check_decryption(rsaObj) - - def test_construct_4tuple(self): - """RSA (default implementation) constructed key (4-tuple)""" - rsaObj = self.rsa.construct((self.n, self.e, self.d, self.p)) - self._check_encryption(rsaObj) - self._check_decryption(rsaObj) - - def test_construct_5tuple(self): - """RSA (default implementation) constructed key (5-tuple)""" - rsaObj = self.rsa.construct((self.n, self.e, self.d, self.p, self.q)) - self._check_private_key(rsaObj) - self._check_encryption(rsaObj) - self._check_decryption(rsaObj) - - def test_construct_6tuple(self): - """RSA (default implementation) constructed key (6-tuple)""" - rsaObj = self.rsa.construct((self.n, self.e, self.d, self.p, self.q, self.u)) - self._check_private_key(rsaObj) - self._check_encryption(rsaObj) - self._check_decryption(rsaObj) - - def test_construct_bad_key2(self): - tup = (self.n, 1) - self.assertRaises(ValueError, self.rsa.construct, tup) - - # An even modulus is wrong - tup = (self.n+1, self.e) - self.assertRaises(ValueError, self.rsa.construct, tup) - - def test_construct_bad_key3(self): - tup = (self.n, self.e, self.d+1) - self.assertRaises(ValueError, self.rsa.construct, tup) - - def test_construct_bad_key5(self): - tup = (self.n, self.e, self.d, self.p, self.p) - self.assertRaises(ValueError, self.rsa.construct, tup) - - tup = (self.p*self.p, self.e, self.p, self.p) - self.assertRaises(ValueError, self.rsa.construct, tup) - - tup = (self.p*self.p, 3, self.p, self.q) - self.assertRaises(ValueError, self.rsa.construct, tup) - - def test_construct_bad_key6(self): - tup = (self.n, self.e, self.d, self.p, self.q, 10) - self.assertRaises(ValueError, self.rsa.construct, tup) - - from Cryptodome.Util.number import inverse - tup = (self.n, self.e, self.d, self.p, self.q, inverse(self.q, self.p)) - self.assertRaises(ValueError, self.rsa.construct, tup) - - def test_factoring(self): - rsaObj = self.rsa.construct([self.n, self.e, self.d]) - self.assertTrue(rsaObj.p==self.p or rsaObj.p==self.q) - self.assertTrue(rsaObj.q==self.p or rsaObj.q==self.q) - self.assertTrue(rsaObj.q*rsaObj.p == self.n) - - self.assertRaises(ValueError, self.rsa.construct, [self.n, self.e, self.n-1]) - - def test_repr(self): - rsaObj = self.rsa.construct((self.n, self.e, self.d, self.p, self.q)) - repr(rsaObj) - - def test_serialization(self): - """RSA keys are unpickable""" - - rsa_key = self.rsa.generate(1024) - self.assertRaises(PicklingError, pickle.dumps, rsa_key) - - def test_raw_rsa_boundary(self): - # The argument of every RSA raw operation (encrypt/decrypt) must be - # non-negative and no larger than the modulus - rsa_obj = self.rsa.generate(1024) - - self.assertRaises(ValueError, rsa_obj._decrypt, rsa_obj.n) - self.assertRaises(ValueError, rsa_obj._decrypt_to_bytes, rsa_obj.n) - self.assertRaises(ValueError, rsa_obj._encrypt, rsa_obj.n) - - self.assertRaises(ValueError, rsa_obj._decrypt, -1) - self.assertRaises(ValueError, rsa_obj._decrypt_to_bytes, -1) - self.assertRaises(ValueError, rsa_obj._encrypt, -1) - - def test_size(self): - pub = self.rsa.construct((self.n, self.e)) - self.assertEqual(pub.size_in_bits(), 1024) - self.assertEqual(pub.size_in_bytes(), 128) - - def _check_private_key(self, rsaObj): - from Cryptodome.Math.Numbers import Integer - - # Check capabilities - self.assertEqual(1, rsaObj.has_private()) - - # Sanity check key data - self.assertEqual(rsaObj.n, rsaObj.p * rsaObj.q) # n = pq - lcm = int(Integer(rsaObj.p-1).lcm(rsaObj.q-1)) - self.assertEqual(1, rsaObj.d * rsaObj.e % lcm) # ed = 1 (mod LCM(p-1, q-1)) - self.assertEqual(1, rsaObj.p * rsaObj.u % rsaObj.q) # pu = 1 (mod q) - self.assertEqual(1, rsaObj.p > 1) # p > 1 - self.assertEqual(1, rsaObj.q > 1) # q > 1 - self.assertEqual(1, rsaObj.e > 1) # e > 1 - self.assertEqual(1, rsaObj.d > 1) # d > 1 - - self.assertEqual(rsaObj.u, rsaObj.invp) - self.assertEqual(1, rsaObj.q * rsaObj.invq % rsaObj.p) - - def _check_public_key(self, rsaObj): - ciphertext = a2b_hex(self.ciphertext) - - # Check capabilities - self.assertEqual(0, rsaObj.has_private()) - - # Check rsaObj.[ne] -> rsaObj.[ne] mapping - self.assertEqual(rsaObj.n, rsaObj.n) - self.assertEqual(rsaObj.e, rsaObj.e) - - # Check that private parameters are all missing - self.assertEqual(0, hasattr(rsaObj, 'd')) - self.assertEqual(0, hasattr(rsaObj, 'p')) - self.assertEqual(0, hasattr(rsaObj, 'q')) - self.assertEqual(0, hasattr(rsaObj, 'u')) - - # Sanity check key data - self.assertEqual(1, rsaObj.e > 1) # e > 1 - - # Public keys should not be able to sign or decrypt - self.assertRaises(TypeError, rsaObj._decrypt, - bytes_to_long(ciphertext)) - self.assertRaises(TypeError, rsaObj._decrypt_to_bytes, - bytes_to_long(ciphertext)) - - # Check __eq__ and __ne__ - self.assertEqual(rsaObj.public_key() == rsaObj.public_key(),True) # assert_ - self.assertEqual(rsaObj.public_key() != rsaObj.public_key(),False) # assertFalse - - self.assertEqual(rsaObj.publickey(), rsaObj.public_key()) - - def _exercise_primitive(self, rsaObj): - # Since we're using a randomly-generated key, we can't check the test - # vector, but we can make sure encryption and decryption are inverse - # operations. - ciphertext = bytes_to_long(a2b_hex(self.ciphertext)) - - # Test decryption - plaintext = rsaObj._decrypt(ciphertext) - - # Test encryption (2 arguments) - new_ciphertext2 = rsaObj._encrypt(plaintext) - self.assertEqual(ciphertext, new_ciphertext2) - - def _exercise_public_primitive(self, rsaObj): - plaintext = a2b_hex(self.plaintext) - - # Test encryption (2 arguments) - new_ciphertext2 = rsaObj._encrypt(bytes_to_long(plaintext)) - - def _check_encryption(self, rsaObj): - plaintext = a2b_hex(self.plaintext) - ciphertext = a2b_hex(self.ciphertext) - - # Test encryption - new_ciphertext2 = rsaObj._encrypt(bytes_to_long(plaintext)) - self.assertEqual(bytes_to_long(ciphertext), new_ciphertext2) - - def _check_decryption(self, rsaObj): - plaintext = bytes_to_long(a2b_hex(self.plaintext)) - ciphertext = bytes_to_long(a2b_hex(self.ciphertext)) - - # Test plain decryption - new_plaintext = rsaObj._decrypt(ciphertext) - self.assertEqual(plaintext, new_plaintext) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(RSATest) - return tests - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_import_DSA.py b/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_import_DSA.py deleted file mode 100644 index 5ff0113..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_import_DSA.py +++ /dev/null @@ -1,554 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/PublicKey/test_import_DSA.py: Self-test for importing DSA keys -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -import unittest -import re - -from Cryptodome.PublicKey import DSA -from Cryptodome.SelfTest.st_common import * -from Cryptodome.Util.py3compat import * - -from binascii import unhexlify - -class ImportKeyTests(unittest.TestCase): - - y = 92137165128186062214622779787483327510946462589285775188003362705875131352591574106484271700740858696583623951844732128165434284507709057439633739849986759064015013893156866539696757799934634945787496920169462601722830899660681779448742875054459716726855443681559131362852474817534616736104831095601710736729 - p = 162452170958135306109773853318304545923250830605675936228618290525164105310663722368377131295055868997377338797580997938253236213714988311430600065853662861806894003694743806769284131194035848116051021923956699231855223389086646903420682639786976554552864568460372266462812137447840653688476258666833303658691 - q = 988791743931120302950649732173330531512663554851 - g = 85583152299197514738065570254868711517748965097380456700369348466136657764813442044039878840094809620913085570225318356734366886985903212775602770761953571967834823306046501307810937486758039063386311593890777319935391363872375452381836756832784184928202587843258855704771836753434368484556809100537243908232 - x = 540873410045082450874416847965843801027716145253 - - def setUp(self): - - # It is easier to write test vectors in text form, - # and convert them to byte strigs dynamically here - for mname, mvalue in ImportKeyTests.__dict__.items(): - if mname[:4] in ('der_', 'pem_', 'ssh_'): - if mname[:4] == 'der_': - mvalue = unhexlify(tobytes(mvalue)) - mvalue = tobytes(mvalue) - setattr(self, mname, mvalue) - - # 1. SubjectPublicKeyInfo - der_public=\ - '308201b73082012b06072a8648ce3804013082011e02818100e756ee1717f4b6'+\ - '794c7c214724a19763742c45572b4b3f8ff3b44f3be9f44ce039a2757695ec91'+\ - '5697da74ef914fcd1b05660e2419c761d639f45d2d79b802dbd23e7ab8b81b47'+\ - '9a380e1f30932584ba2a0b955032342ebc83cb5ca906e7b0d7cd6fe656cecb4c'+\ - '8b5a77123a8c6750a481e3b06057aff6aa6eba620b832d60c3021500ad32f48c'+\ - 'd3ae0c45a198a61fa4b5e20320763b2302818079dfdc3d614fe635fceb7eaeae'+\ - '3718dc2efefb45282993ac6749dc83c223d8c1887296316b3b0b54466cf444f3'+\ - '4b82e3554d0b90a778faaf1306f025dae6a3e36c7f93dd5bac4052b92370040a'+\ - 'ca70b8d5820599711900efbc961812c355dd9beffe0981da85c5548074b41c56'+\ - 'ae43fd300d89262e4efd89943f99a651b03888038185000281810083352a69a1'+\ - '32f34843d2a0eb995bff4e2f083a73f0049d2c91ea2f0ce43d144abda48199e4'+\ - 'b003c570a8af83303d45105f606c5c48d925a40ed9c2630c2fa4cdbf838539de'+\ - 'b9a29f919085f2046369f627ca84b2cb1e2c7940564b670f963ab1164d4e2ca2'+\ - 'bf6ffd39f12f548928bf4d2d1b5e6980b4f1be4c92a91986fba559' - - def testImportKey1(self): - key_obj = DSA.importKey(self.der_public) - self.assertFalse(key_obj.has_private()) - self.assertEqual(self.y, key_obj.y) - self.assertEqual(self.p, key_obj.p) - self.assertEqual(self.q, key_obj.q) - self.assertEqual(self.g, key_obj.g) - - def testExportKey1(self): - tup = (self.y, self.g, self.p, self.q) - key = DSA.construct(tup) - encoded = key.export_key('DER') - self.assertEqual(self.der_public, encoded) - - # 2. - pem_public="""\ ------BEGIN PUBLIC KEY----- -MIIBtzCCASsGByqGSM44BAEwggEeAoGBAOdW7hcX9LZ5THwhRyShl2N0LEVXK0s/ -j/O0Tzvp9EzgOaJ1dpXskVaX2nTvkU/NGwVmDiQZx2HWOfRdLXm4AtvSPnq4uBtH -mjgOHzCTJYS6KguVUDI0LryDy1ypBuew181v5lbOy0yLWncSOoxnUKSB47BgV6/2 -qm66YguDLWDDAhUArTL0jNOuDEWhmKYfpLXiAyB2OyMCgYB539w9YU/mNfzrfq6u -NxjcLv77RSgpk6xnSdyDwiPYwYhyljFrOwtURmz0RPNLguNVTQuQp3j6rxMG8CXa -5qPjbH+T3VusQFK5I3AECspwuNWCBZlxGQDvvJYYEsNV3Zvv/gmB2oXFVIB0tBxW -rkP9MA2JJi5O/YmUP5mmUbA4iAOBhQACgYEAgzUqaaEy80hD0qDrmVv/Ti8IOnPw -BJ0skeovDOQ9FEq9pIGZ5LADxXCor4MwPUUQX2BsXEjZJaQO2cJjDC+kzb+DhTne -uaKfkZCF8gRjafYnyoSyyx4seUBWS2cPljqxFk1OLKK/b/058S9UiSi/TS0bXmmA -tPG+TJKpGYb7pVk= ------END PUBLIC KEY-----""" - - def testImportKey2(self): - for pem in (self.pem_public, tostr(self.pem_public)): - key_obj = DSA.importKey(pem) - self.assertFalse(key_obj.has_private()) - self.assertEqual(self.y, key_obj.y) - self.assertEqual(self.p, key_obj.p) - self.assertEqual(self.q, key_obj.q) - self.assertEqual(self.g, key_obj.g) - - def testExportKey2(self): - tup = (self.y, self.g, self.p, self.q) - key = DSA.construct(tup) - encoded = key.export_key('PEM') - self.assertEqual(self.pem_public, encoded) - - # 3. OpenSSL/OpenSSH format - der_private=\ - '308201bb02010002818100e756ee1717f4b6794c7c214724a19763742c45572b'+\ - '4b3f8ff3b44f3be9f44ce039a2757695ec915697da74ef914fcd1b05660e2419'+\ - 'c761d639f45d2d79b802dbd23e7ab8b81b479a380e1f30932584ba2a0b955032'+\ - '342ebc83cb5ca906e7b0d7cd6fe656cecb4c8b5a77123a8c6750a481e3b06057'+\ - 'aff6aa6eba620b832d60c3021500ad32f48cd3ae0c45a198a61fa4b5e2032076'+\ - '3b2302818079dfdc3d614fe635fceb7eaeae3718dc2efefb45282993ac6749dc'+\ - '83c223d8c1887296316b3b0b54466cf444f34b82e3554d0b90a778faaf1306f0'+\ - '25dae6a3e36c7f93dd5bac4052b92370040aca70b8d5820599711900efbc9618'+\ - '12c355dd9beffe0981da85c5548074b41c56ae43fd300d89262e4efd89943f99'+\ - 'a651b038880281810083352a69a132f34843d2a0eb995bff4e2f083a73f0049d'+\ - '2c91ea2f0ce43d144abda48199e4b003c570a8af83303d45105f606c5c48d925'+\ - 'a40ed9c2630c2fa4cdbf838539deb9a29f919085f2046369f627ca84b2cb1e2c'+\ - '7940564b670f963ab1164d4e2ca2bf6ffd39f12f548928bf4d2d1b5e6980b4f1'+\ - 'be4c92a91986fba55902145ebd9a3f0b82069d98420986b314215025756065' - - def testImportKey3(self): - key_obj = DSA.importKey(self.der_private) - self.assertTrue(key_obj.has_private()) - self.assertEqual(self.y, key_obj.y) - self.assertEqual(self.p, key_obj.p) - self.assertEqual(self.q, key_obj.q) - self.assertEqual(self.g, key_obj.g) - self.assertEqual(self.x, key_obj.x) - - def testExportKey3(self): - tup = (self.y, self.g, self.p, self.q, self.x) - key = DSA.construct(tup) - encoded = key.export_key('DER', pkcs8=False) - self.assertEqual(self.der_private, encoded) - - # 4. - pem_private="""\ ------BEGIN DSA PRIVATE KEY----- -MIIBuwIBAAKBgQDnVu4XF/S2eUx8IUckoZdjdCxFVytLP4/ztE876fRM4DmidXaV -7JFWl9p075FPzRsFZg4kGcdh1jn0XS15uALb0j56uLgbR5o4Dh8wkyWEuioLlVAy -NC68g8tcqQbnsNfNb+ZWzstMi1p3EjqMZ1CkgeOwYFev9qpuumILgy1gwwIVAK0y -9IzTrgxFoZimH6S14gMgdjsjAoGAed/cPWFP5jX8636urjcY3C7++0UoKZOsZ0nc -g8Ij2MGIcpYxazsLVEZs9ETzS4LjVU0LkKd4+q8TBvAl2uaj42x/k91brEBSuSNw -BArKcLjVggWZcRkA77yWGBLDVd2b7/4JgdqFxVSAdLQcVq5D/TANiSYuTv2JlD+Z -plGwOIgCgYEAgzUqaaEy80hD0qDrmVv/Ti8IOnPwBJ0skeovDOQ9FEq9pIGZ5LAD -xXCor4MwPUUQX2BsXEjZJaQO2cJjDC+kzb+DhTneuaKfkZCF8gRjafYnyoSyyx4s -eUBWS2cPljqxFk1OLKK/b/058S9UiSi/TS0bXmmAtPG+TJKpGYb7pVkCFF69mj8L -ggadmEIJhrMUIVAldWBl ------END DSA PRIVATE KEY-----""" - - def testImportKey4(self): - for pem in (self.pem_private, tostr(self.pem_private)): - key_obj = DSA.importKey(pem) - self.assertTrue(key_obj.has_private()) - self.assertEqual(self.y, key_obj.y) - self.assertEqual(self.p, key_obj.p) - self.assertEqual(self.q, key_obj.q) - self.assertEqual(self.g, key_obj.g) - self.assertEqual(self.x, key_obj.x) - - def testExportKey4(self): - tup = (self.y, self.g, self.p, self.q, self.x) - key = DSA.construct(tup) - encoded = key.export_key('PEM', pkcs8=False) - self.assertEqual(self.pem_private, encoded) - - # 5. PKCS8 (unencrypted) - der_pkcs8=\ - '3082014a0201003082012b06072a8648ce3804013082011e02818100e756ee17'+\ - '17f4b6794c7c214724a19763742c45572b4b3f8ff3b44f3be9f44ce039a27576'+\ - '95ec915697da74ef914fcd1b05660e2419c761d639f45d2d79b802dbd23e7ab8'+\ - 'b81b479a380e1f30932584ba2a0b955032342ebc83cb5ca906e7b0d7cd6fe656'+\ - 'cecb4c8b5a77123a8c6750a481e3b06057aff6aa6eba620b832d60c3021500ad'+\ - '32f48cd3ae0c45a198a61fa4b5e20320763b2302818079dfdc3d614fe635fceb'+\ - '7eaeae3718dc2efefb45282993ac6749dc83c223d8c1887296316b3b0b54466c'+\ - 'f444f34b82e3554d0b90a778faaf1306f025dae6a3e36c7f93dd5bac4052b923'+\ - '70040aca70b8d5820599711900efbc961812c355dd9beffe0981da85c5548074'+\ - 'b41c56ae43fd300d89262e4efd89943f99a651b03888041602145ebd9a3f0b82'+\ - '069d98420986b314215025756065' - - def testImportKey5(self): - key_obj = DSA.importKey(self.der_pkcs8) - self.assertTrue(key_obj.has_private()) - self.assertEqual(self.y, key_obj.y) - self.assertEqual(self.p, key_obj.p) - self.assertEqual(self.q, key_obj.q) - self.assertEqual(self.g, key_obj.g) - self.assertEqual(self.x, key_obj.x) - - def testExportKey5(self): - tup = (self.y, self.g, self.p, self.q, self.x) - key = DSA.construct(tup) - encoded = key.export_key('DER') - self.assertEqual(self.der_pkcs8, encoded) - encoded = key.export_key('DER', pkcs8=True) - self.assertEqual(self.der_pkcs8, encoded) - - # 6. - pem_pkcs8="""\ ------BEGIN PRIVATE KEY----- -MIIBSgIBADCCASsGByqGSM44BAEwggEeAoGBAOdW7hcX9LZ5THwhRyShl2N0LEVX -K0s/j/O0Tzvp9EzgOaJ1dpXskVaX2nTvkU/NGwVmDiQZx2HWOfRdLXm4AtvSPnq4 -uBtHmjgOHzCTJYS6KguVUDI0LryDy1ypBuew181v5lbOy0yLWncSOoxnUKSB47Bg -V6/2qm66YguDLWDDAhUArTL0jNOuDEWhmKYfpLXiAyB2OyMCgYB539w9YU/mNfzr -fq6uNxjcLv77RSgpk6xnSdyDwiPYwYhyljFrOwtURmz0RPNLguNVTQuQp3j6rxMG -8CXa5qPjbH+T3VusQFK5I3AECspwuNWCBZlxGQDvvJYYEsNV3Zvv/gmB2oXFVIB0 -tBxWrkP9MA2JJi5O/YmUP5mmUbA4iAQWAhRevZo/C4IGnZhCCYazFCFQJXVgZQ== ------END PRIVATE KEY-----""" - - def testImportKey6(self): - for pem in (self.pem_pkcs8, tostr(self.pem_pkcs8)): - key_obj = DSA.importKey(pem) - self.assertTrue(key_obj.has_private()) - self.assertEqual(self.y, key_obj.y) - self.assertEqual(self.p, key_obj.p) - self.assertEqual(self.q, key_obj.q) - self.assertEqual(self.g, key_obj.g) - self.assertEqual(self.x, key_obj.x) - - def testExportKey6(self): - tup = (self.y, self.g, self.p, self.q, self.x) - key = DSA.construct(tup) - encoded = key.export_key('PEM') - self.assertEqual(self.pem_pkcs8, encoded) - encoded = key.export_key('PEM', pkcs8=True) - self.assertEqual(self.pem_pkcs8, encoded) - - # 7. OpenSSH/RFC4253 - ssh_pub="""ssh-dss AAAAB3NzaC1kc3MAAACBAOdW7hcX9LZ5THwhRyShl2N0LEVXK0s/j/O0Tzvp9EzgOaJ1dpXskVaX2nTvkU/NGwVmDiQZx2HWOfRdLXm4AtvSPnq4uBtHmjgOHzCTJYS6KguVUDI0LryDy1ypBuew181v5lbOy0yLWncSOoxnUKSB47BgV6/2qm66YguDLWDDAAAAFQCtMvSM064MRaGYph+kteIDIHY7IwAAAIB539w9YU/mNfzrfq6uNxjcLv77RSgpk6xnSdyDwiPYwYhyljFrOwtURmz0RPNLguNVTQuQp3j6rxMG8CXa5qPjbH+T3VusQFK5I3AECspwuNWCBZlxGQDvvJYYEsNV3Zvv/gmB2oXFVIB0tBxWrkP9MA2JJi5O/YmUP5mmUbA4iAAAAIEAgzUqaaEy80hD0qDrmVv/Ti8IOnPwBJ0skeovDOQ9FEq9pIGZ5LADxXCor4MwPUUQX2BsXEjZJaQO2cJjDC+kzb+DhTneuaKfkZCF8gRjafYnyoSyyx4seUBWS2cPljqxFk1OLKK/b/058S9UiSi/TS0bXmmAtPG+TJKpGYb7pVk=""" - - def testImportKey7(self): - for ssh in (self.ssh_pub, tostr(self.ssh_pub)): - key_obj = DSA.importKey(ssh) - self.assertFalse(key_obj.has_private()) - self.assertEqual(self.y, key_obj.y) - self.assertEqual(self.p, key_obj.p) - self.assertEqual(self.q, key_obj.q) - self.assertEqual(self.g, key_obj.g) - - def testExportKey7(self): - tup = (self.y, self.g, self.p, self.q) - key = DSA.construct(tup) - encoded = key.export_key('OpenSSH') - self.assertEqual(self.ssh_pub, encoded) - - # 8. Encrypted OpenSSL/OpenSSH - pem_private_encrypted="""\ ------BEGIN DSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: AES-128-CBC,70B6908939D65E9F2EB999E8729788CE - -4V6GHRDpCrdZ8MBjbyp5AlGUrjvr2Pn2e2zVxy5RBt4FBj9/pa0ae0nnyUPMLSUU -kKyOR0topRYTVRLElm4qVrb5uNZ3hRwfbklr+pSrB7O9eHz9V5sfOQxyODS07JxK -k1OdOs70/ouMXLF9EWfAZOmWUccZKHNblUwg1p1UrZIz5jXw4dUE/zqhvXh6d+iC -ADsICaBCjCrRQJKDp50h3+ndQjkYBKVH+pj8TiQ79U7lAvdp3+iMghQN6YXs9mdI -gFpWw/f97oWM4GHZFqHJ+VSMNFjBiFhAvYV587d7Lk4dhD8sCfbxj42PnfRgUItc -nnPqHxmhMQozBWzYM4mQuo3XbF2WlsNFbOzFVyGhw1Bx1s91qvXBVWJh2ozrW0s6 -HYDV7ZkcTml/4kjA/d+mve6LZ8kuuR1qCiZx6rkffhh1gDN/1Xz3HVvIy/dQ+h9s -5zp7PwUoWbhqp3WCOr156P6gR8qo7OlT6wMh33FSXK/mxikHK136fV2shwTKQVII -rJBvXpj8nACUmi7scKuTWGeUoXa+dwTZVVe+b+L2U1ZM7+h/neTJiXn7u99PFUwu -xVJtxaV37m3aXxtCsPnbBg== ------END DSA PRIVATE KEY-----""" - - def testImportKey8(self): - for pem in (self.pem_private_encrypted, tostr(self.pem_private_encrypted)): - key_obj = DSA.importKey(pem, "PWDTEST") - self.assertTrue(key_obj.has_private()) - self.assertEqual(self.y, key_obj.y) - self.assertEqual(self.p, key_obj.p) - self.assertEqual(self.q, key_obj.q) - self.assertEqual(self.g, key_obj.g) - self.assertEqual(self.x, key_obj.x) - - def testExportKey8(self): - tup = (self.y, self.g, self.p, self.q, self.x) - key = DSA.construct(tup) - encoded = key.export_key('PEM', pkcs8=False, passphrase="PWDTEST") - key = DSA.importKey(encoded, "PWDTEST") - self.assertEqual(self.y, key.y) - self.assertEqual(self.p, key.p) - self.assertEqual(self.q, key.q) - self.assertEqual(self.g, key.g) - self.assertEqual(self.x, key.x) - - # 9. Encrypted PKCS8 - # pbeWithMD5AndDES-CBC - pem_pkcs8_encrypted="""\ ------BEGIN ENCRYPTED PRIVATE KEY----- -MIIBcTAbBgkqhkiG9w0BBQMwDgQI0GC3BJ/jSw8CAggABIIBUHc1cXZpExIE9tC7 -7ryiW+5ihtF2Ekurq3e408GYSAu5smJjN2bvQXmzRFBz8W38K8eMf1sbWroZ4+zn -kZSbb9nSm5kAa8lR2+oF2k+WRswMR/PTC3f/D9STO2X0QxdrzKgIHEcSGSHp5jTx -aVvbkCDHo9vhBTl6S3ogZ48As/MEro76+9igUwJ1jNhIQZPJ7e20QH5qDpQFFJN4 -CKl2ENSEuwGiqBszItFy4dqH0g63ZGZV/xt9wSO9Rd7SK/EbA/dklOxBa5Y/VItM -gnIhs9XDMoGYyn6F023EicNJm6g/bVQk81BTTma4tm+12TKGdYm+QkeZvCOMZylr -Wv67cKwO3cAXt5C3QXMDgYR64XvuaT5h7C0igMp2afSXJlnbHEbFxQVJlv83T4FM -eZ4k+NQDbEL8GiHmFxzDWQAuPPZKJWEEEV2p/To+WOh+kSDHQw== ------END ENCRYPTED PRIVATE KEY-----""" - - def testImportKey9(self): - for pem in (self.pem_pkcs8_encrypted, tostr(self.pem_pkcs8_encrypted)): - key_obj = DSA.importKey(pem, "PWDTEST") - self.assertTrue(key_obj.has_private()) - self.assertEqual(self.y, key_obj.y) - self.assertEqual(self.p, key_obj.p) - self.assertEqual(self.q, key_obj.q) - self.assertEqual(self.g, key_obj.g) - self.assertEqual(self.x, key_obj.x) - - # 10. Encrypted PKCS8 - # pkcs5PBES2 / - # pkcs5PBKDF2 (rounds=1000, salt=D725BF1B6B8239F4) / - # des-EDE3-CBC (iv=27A1C66C42AFEECE) - # - der_pkcs8_encrypted=\ - '30820196304006092a864886f70d01050d3033301b06092a864886f70d01050c'+\ - '300e0408d725bf1b6b8239f4020203e8301406082a864886f70d0307040827a1'+\ - 'c66c42afeece048201505cacfde7bf8edabb3e0d387950dc872662ea7e9b1ed4'+\ - '400d2e7e6186284b64668d8d0328c33a9d9397e6f03df7cb68268b0a06b4e22f'+\ - '7d132821449ecf998a8b696dbc6dd2b19e66d7eb2edfeb4153c1771d49702395'+\ - '4f36072868b5fcccf93413a5ac4b2eb47d4b3f681c6bd67ae363ed776f45ae47'+\ - '174a00098a7c930a50f820b227ddf50f9742d8e950d02586ff2dac0e3c372248'+\ - 'e5f9b6a7a02f4004f20c87913e0f7b52bccc209b95d478256a890b31d4c9adec'+\ - '21a4d157a179a93a3dad06f94f3ce486b46dfa7fc15fd852dd7680bbb2f17478'+\ - '7e71bd8dbaf81eca7518d76c1d26256e95424864ba45ca5d47d7c5a421be02fa'+\ - 'b94ab01e18593f66cf9094eb5c94b9ecf3aa08b854a195cf87612fbe5e96c426'+\ - '2b0d573e52dc71ba3f5e468c601e816c49b7d32c698b22175e89aaef0c443770'+\ - '5ef2f88a116d99d8e2869a4fd09a771b84b49e4ccb79aadcb1c9' - - def testImportKey10(self): - key_obj = DSA.importKey(self.der_pkcs8_encrypted, "PWDTEST") - self.assertTrue(key_obj.has_private()) - self.assertEqual(self.y, key_obj.y) - self.assertEqual(self.p, key_obj.p) - self.assertEqual(self.q, key_obj.q) - self.assertEqual(self.g, key_obj.g) - self.assertEqual(self.x, key_obj.x) - - def testExportKey10(self): - tup = (self.y, self.g, self.p, self.q, self.x) - key = DSA.construct(tup) - randfunc = BytesIO(unhexlify(b("27A1C66C42AFEECE") + b("D725BF1B6B8239F4"))).read - encoded = key.export_key('DER', pkcs8=True, passphrase="PWDTEST", randfunc=randfunc) - self.assertEqual(self.der_pkcs8_encrypted, encoded) - - # ---- - - def testImportError1(self): - self.assertRaises(ValueError, DSA.importKey, self.der_pkcs8_encrypted, "wrongpwd") - - def testExportError2(self): - tup = (self.y, self.g, self.p, self.q, self.x) - key = DSA.construct(tup) - self.assertRaises(ValueError, key.export_key, 'DER', pkcs8=False, passphrase="PWDTEST") - - def test_import_key(self): - """Verify importKey is an alias to import_key""" - - key_obj = DSA.import_key(self.der_public) - self.assertFalse(key_obj.has_private()) - self.assertEqual(self.y, key_obj.y) - self.assertEqual(self.p, key_obj.p) - self.assertEqual(self.q, key_obj.q) - self.assertEqual(self.g, key_obj.g) - - def test_exportKey(self): - tup = (self.y, self.g, self.p, self.q, self.x) - key = DSA.construct(tup) - self.assertEqual(key.exportKey(), key.export_key()) - - - def test_import_empty(self): - self.assertRaises(ValueError, DSA.import_key, b'') - - -class ImportKeyFromX509Cert(unittest.TestCase): - - def test_x509v1(self): - - # Sample V1 certificate with a 1024 bit DSA key - x509_v1_cert = """ ------BEGIN CERTIFICATE----- -MIIDUjCCArsCAQIwDQYJKoZIhvcNAQEFBQAwfjENMAsGA1UEChMEQWNtZTELMAkG -A1UECxMCUkQxHDAaBgkqhkiG9w0BCQEWDXNwYW1AYWNtZS5vcmcxEzARBgNVBAcT -Ck1ldHJvcG9saXMxETAPBgNVBAgTCE5ldyBZb3JrMQswCQYDVQQGEwJVUzENMAsG -A1UEAxMEdGVzdDAeFw0xNDA3MTEyMDM4NDNaFw0xNzA0MDYyMDM4NDNaME0xCzAJ -BgNVBAYTAlVTMREwDwYDVQQIEwhOZXcgWW9yazENMAsGA1UEChMEQWNtZTELMAkG -A1UECxMCUkQxDzANBgNVBAMTBnBvbGFuZDCCAbYwggErBgcqhkjOOAQBMIIBHgKB -gQDOrN4Ox4+t3T6wKeHfhzArhcrNEFMQ4Ss+4PIKyimDy9Bn64WPkL1B/9dvYIga -23GLu6tVJmXo6EdJnVOHEMhr99EeOwuDWWeP7Awq7RSlKEejokr4BEzMTW/tExSD -cO6/GI7xzh0eTH+VTTPDfyrJMYCkh0rJAfCP+5xrmPNetwIVALtXYOV1yoRrzJ2Q -M5uEjidH6GiZAoGAfUqA1SAm5g5U68SILMVX9l5rq0OpB0waBMpJQ31/R/yXNDqo -c3gGWZTOJFU4IzwNpGhrGNADUByz/lc1SAOAdEJIr0JVrhbGewQjB4pWqoLGbBKz -RoavTNDc/zD7SYa12evWDHADwvlXoeQg+lWop1zS8OqaDC7aLGKpWN3/m8kDgYQA -AoGAKoirPAfcp1rbbl4y2FFAIktfW8f4+T7d2iKSg73aiVfujhNOt1Zz1lfC0NI2 -eonLWO3tAM4XGKf1TLjb5UXngGn40okPsaA81YE6ZIKm20ywjlOY3QkAEdMaLVY3 -9PJvM8RGB9m7pLKxyHfGMfF40MVN4222zKeGp7xhM0CNiCUwDQYJKoZIhvcNAQEF -BQADgYEAfbNZfpYa2KlALEM1FZnwvQDvJHntHz8LdeJ4WM7CXDlKi67wY2HKM30w -s2xej75imkVOFd1kF2d0A8sjfriXLVIt1Hwq9ANZomhu4Edx0xpH8tqdh/bDtnM2 -TmduZNY9OWkb07h0CtWD6Zt8fhRllVsSSrlWd/2or7FXNC5weFQ= ------END CERTIFICATE----- - """.strip() - - # DSA public key as dumped by openssl - y_str = """ -2a:88:ab:3c:07:dc:a7:5a:db:6e:5e:32:d8:51:40: -22:4b:5f:5b:c7:f8:f9:3e:dd:da:22:92:83:bd:da: -89:57:ee:8e:13:4e:b7:56:73:d6:57:c2:d0:d2:36: -7a:89:cb:58:ed:ed:00:ce:17:18:a7:f5:4c:b8:db: -e5:45:e7:80:69:f8:d2:89:0f:b1:a0:3c:d5:81:3a: -64:82:a6:db:4c:b0:8e:53:98:dd:09:00:11:d3:1a: -2d:56:37:f4:f2:6f:33:c4:46:07:d9:bb:a4:b2:b1: -c8:77:c6:31:f1:78:d0:c5:4d:e3:6d:b6:cc:a7:86: -a7:bc:61:33:40:8d:88:25 - """ - p_str = """ -00:ce:ac:de:0e:c7:8f:ad:dd:3e:b0:29:e1:df:87: -30:2b:85:ca:cd:10:53:10:e1:2b:3e:e0:f2:0a:ca: -29:83:cb:d0:67:eb:85:8f:90:bd:41:ff:d7:6f:60: -88:1a:db:71:8b:bb:ab:55:26:65:e8:e8:47:49:9d: -53:87:10:c8:6b:f7:d1:1e:3b:0b:83:59:67:8f:ec: -0c:2a:ed:14:a5:28:47:a3:a2:4a:f8:04:4c:cc:4d: -6f:ed:13:14:83:70:ee:bf:18:8e:f1:ce:1d:1e:4c: -7f:95:4d:33:c3:7f:2a:c9:31:80:a4:87:4a:c9:01: -f0:8f:fb:9c:6b:98:f3:5e:b7 - """ - q_str = """ -00:bb:57:60:e5:75:ca:84:6b:cc:9d:90:33:9b:84: -8e:27:47:e8:68:99 - """ - g_str = """ -7d:4a:80:d5:20:26:e6:0e:54:eb:c4:88:2c:c5:57: -f6:5e:6b:ab:43:a9:07:4c:1a:04:ca:49:43:7d:7f: -47:fc:97:34:3a:a8:73:78:06:59:94:ce:24:55:38: -23:3c:0d:a4:68:6b:18:d0:03:50:1c:b3:fe:57:35: -48:03:80:74:42:48:af:42:55:ae:16:c6:7b:04:23: -07:8a:56:aa:82:c6:6c:12:b3:46:86:af:4c:d0:dc: -ff:30:fb:49:86:b5:d9:eb:d6:0c:70:03:c2:f9:57: -a1:e4:20:fa:55:a8:a7:5c:d2:f0:ea:9a:0c:2e:da: -2c:62:a9:58:dd:ff:9b:c9 - """ - - key = DSA.importKey(x509_v1_cert) - for comp_name in ('y', 'p', 'q', 'g'): - comp_str = locals()[comp_name + "_str"] - comp = int(re.sub("[^0-9a-f]", "", comp_str), 16) - self.assertEqual(getattr(key, comp_name), comp) - self.assertFalse(key.has_private()) - - def test_x509v3(self): - - # Sample V3 certificate with a 1024 bit DSA key - x509_v3_cert = """ ------BEGIN CERTIFICATE----- -MIIFhjCCA26gAwIBAgIBAzANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJVUzEL -MAkGA1UECAwCTUQxEjAQBgNVBAcMCUJhbHRpbW9yZTEQMA4GA1UEAwwHVGVzdCBD -QTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbTAeFw0xNDA3MTMyMDUz -MjBaFw0xNzA0MDgyMDUzMjBaMEAxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNRDES -MBAGA1UEBwwJQmFsdGltb3JlMRAwDgYDVQQDDAdhdXN0cmlhMIIBtjCCASsGByqG -SM44BAEwggEeAoGBALfd8gyEpVPA0ZI69Kp3nyJcu5N0ZZ3K1K9hleQLNqKEcZOh -7a/C2J1TPdmHTLJ0rAwBZ1nWxnARSgRphziGDFspKCYQwYcSMz8KoFgvXbXpuchy -oFACiQ2LqZnc5MakuLQtLcQciSYGYj3zmZdYMoa904F1aDWr+DxQI6DVC3/bAhUA -hqXMCJ6fQK3G2O9S3/CC/yVZXCsCgYBRXROl3R2khX7l10LQjDEgo3B1IzjXU/jP -McMBl6XO+nBJXxr/scbq8Ajiv7LTnGpSjgryHtvfj887kfvo8QbSS3kp3vq5uSqI -ui7E7r3jguWaLj616AG1HWOctXJUjqsiabZwsp2h09gHTzmHEXBOmiARu8xFxKAH -xsuo7onAbwOBhAACgYBylWjWSnKHE8mHx1A5m/0GQx6xnhWIe3+MJAnEhRGxA2J4 -SCsfWU0OwglIQToh1z5uUU9oDi9cYgNPBevOFRnDhc2yaJY6VAYnI+D+6J5IU6Yd -0iaG/iSc4sV4bFr0axcPpse3SN0XaQxiKeSFBfFnoMqL+dd9Gb3QPZSllBcVD6OB -1TCB0jAdBgNVHQ4EFgQUx5wN0Puotv388M9Tp/fsPbZpzAUwHwYDVR0jBBgwFoAU -a0hkif3RMaraiWtsOOZZlLu9wJwwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwSgYD -VR0RBEMwQYILZXhhbXBsZS5jb22CD3d3dy5leGFtcGxlLmNvbYIQbWFpbC5leGFt -cGxlLmNvbYIPZnRwLmV4YW1wbGUuY29tMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NM -IEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQsFAAOCAgEAyWf1TiJI -aNEIA9o/PG8/JiGASTS2/HBVTJbkq03k6NkJVk/GxC1DPziTUJ+CdWlHWcAi1EOW -Ach3QxNDRrVfCOfCMDgElIO1094/reJgdFYG00LRi8QkRJuxANV7YS4tLudhyHJC -kR2lhdMNmEuzWK+s2y+5cLrdm7qdvdENQCcV67uvGPx4sc+EaE7x13SczKjWBtbo -QCs6JTOW+EkPRl4Zo27K4OIZ43/J+GxvwU9QUVH3wPVdbbLNw+QeTFBYMTEcxyc4 -kv50HPBFaithziXBFyvdIs19FjkFzu0Uz/e0zb1+vMzQlJMD94HVOrMnIj5Sb2cL -KKdYXS4uhxFJmdV091Xur5JkYYwEzuaGav7J3zOzYutrIGTgDluLCvA+VQkRcTsy -jZ065SkY/v+38QHp+cmm8WRluupJTs8wYzVp6Fu0iFaaK7ztFmaZmHpiPIfDFjva -aCIgzzT5NweJd/b71A2SyzHXJ14zBXsr1PMylMp2TpHIidhuuNuQL6I0HaollB4M -Z3FsVBMhVDw4Z76qnFPr8mZE2tar33hSlJI/3pS/bBiukuBk8U7VB0X8OqaUnP3C -7b2Z4G8GtqDVcKGMzkvMjT4n9rKd/Le+qHSsQOGO9W/0LB7UDAZSwUsfAPnoBgdS -5t9tIomLCOstByXi+gGZue1TcdCa3Ph4kO0= ------END CERTIFICATE----- - """.strip() - - # DSA public key as dumped by openssl - y_str = """ -72:95:68:d6:4a:72:87:13:c9:87:c7:50:39:9b:fd: -06:43:1e:b1:9e:15:88:7b:7f:8c:24:09:c4:85:11: -b1:03:62:78:48:2b:1f:59:4d:0e:c2:09:48:41:3a: -21:d7:3e:6e:51:4f:68:0e:2f:5c:62:03:4f:05:eb: -ce:15:19:c3:85:cd:b2:68:96:3a:54:06:27:23:e0: -fe:e8:9e:48:53:a6:1d:d2:26:86:fe:24:9c:e2:c5: -78:6c:5a:f4:6b:17:0f:a6:c7:b7:48:dd:17:69:0c: -62:29:e4:85:05:f1:67:a0:ca:8b:f9:d7:7d:19:bd: -d0:3d:94:a5:94:17:15:0f - """ - p_str = """ -00:b7:dd:f2:0c:84:a5:53:c0:d1:92:3a:f4:aa:77: -9f:22:5c:bb:93:74:65:9d:ca:d4:af:61:95:e4:0b: -36:a2:84:71:93:a1:ed:af:c2:d8:9d:53:3d:d9:87: -4c:b2:74:ac:0c:01:67:59:d6:c6:70:11:4a:04:69: -87:38:86:0c:5b:29:28:26:10:c1:87:12:33:3f:0a: -a0:58:2f:5d:b5:e9:b9:c8:72:a0:50:02:89:0d:8b: -a9:99:dc:e4:c6:a4:b8:b4:2d:2d:c4:1c:89:26:06: -62:3d:f3:99:97:58:32:86:bd:d3:81:75:68:35:ab: -f8:3c:50:23:a0:d5:0b:7f:db - """ - q_str = """ -00:86:a5:cc:08:9e:9f:40:ad:c6:d8:ef:52:df:f0: -82:ff:25:59:5c:2b - """ - g_str = """ -51:5d:13:a5:dd:1d:a4:85:7e:e5:d7:42:d0:8c:31: -20:a3:70:75:23:38:d7:53:f8:cf:31:c3:01:97:a5: -ce:fa:70:49:5f:1a:ff:b1:c6:ea:f0:08:e2:bf:b2: -d3:9c:6a:52:8e:0a:f2:1e:db:df:8f:cf:3b:91:fb: -e8:f1:06:d2:4b:79:29:de:fa:b9:b9:2a:88:ba:2e: -c4:ee:bd:e3:82:e5:9a:2e:3e:b5:e8:01:b5:1d:63: -9c:b5:72:54:8e:ab:22:69:b6:70:b2:9d:a1:d3:d8: -07:4f:39:87:11:70:4e:9a:20:11:bb:cc:45:c4:a0: -07:c6:cb:a8:ee:89:c0:6f - """ - - key = DSA.importKey(x509_v3_cert) - for comp_name in ('y', 'p', 'q', 'g'): - comp_str = locals()[comp_name + "_str"] - comp = int(re.sub("[^0-9a-f]", "", comp_str), 16) - self.assertEqual(getattr(key, comp_name), comp) - self.assertFalse(key.has_private()) - - -if __name__ == '__main__': - unittest.main() - -def get_tests(config={}): - tests = [] - tests += list_test_cases(ImportKeyTests) - tests += list_test_cases(ImportKeyFromX509Cert) - return tests - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - diff --git a/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_import_ECC.py b/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_import_ECC.py deleted file mode 100644 index 01297f8..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_import_ECC.py +++ /dev/null @@ -1,2763 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2015, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import os -import errno -import warnings -import unittest -from binascii import unhexlify - -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.Util.py3compat import bord, tostr, FileNotFoundError -from Cryptodome.Util.asn1 import DerSequence, DerBitString -from Cryptodome.Util.number import bytes_to_long -from Cryptodome.Hash import SHAKE128 - -from Cryptodome.PublicKey import ECC - -try: - import pycryptodome_test_vectors # type: ignore - test_vectors_available = True -except ImportError: - test_vectors_available = False - - -class MissingTestVectorException(ValueError): - pass - - -def load_file(file_name, mode="rb"): - results = None - - try: - if not test_vectors_available: - raise FileNotFoundError(errno.ENOENT, - os.strerror(errno.ENOENT), - file_name) - - dir_comps = ("PublicKey", "ECC") - init_dir = os.path.dirname(pycryptodome_test_vectors.__file__) - full_file_name = os.path.join(os.path.join(init_dir, *dir_comps), file_name) - with open(full_file_name, mode) as file_in: - results = file_in.read() - - except FileNotFoundError: - warnings.warn("Warning: skipping extended tests for ECC", - UserWarning, - stacklevel=2) - - if results is None: - raise MissingTestVectorException("Missing %s" % file_name) - - return results - - -def compact(lines): - ext = b"".join(lines) - return unhexlify(tostr(ext).replace(" ", "").replace(":", "")) - - -def create_ref_keys_p192(): - key_len = 24 - key_lines = load_file("ecc_p192.txt").splitlines() - private_key_d = bytes_to_long(compact(key_lines[2:4])) - public_key_xy = compact(key_lines[5:9]) - assert bord(public_key_xy[0]) == 4 # Uncompressed - public_key_x = bytes_to_long(public_key_xy[1:key_len+1]) - public_key_y = bytes_to_long(public_key_xy[key_len+1:]) - - return (ECC.construct(curve="P-192", d=private_key_d), - ECC.construct(curve="P-192", point_x=public_key_x, point_y=public_key_y)) - - -def create_ref_keys_p224(): - key_len = 28 - key_lines = load_file("ecc_p224.txt").splitlines() - private_key_d = bytes_to_long(compact(key_lines[2:4])) - public_key_xy = compact(key_lines[5:9]) - assert bord(public_key_xy[0]) == 4 # Uncompressed - public_key_x = bytes_to_long(public_key_xy[1:key_len+1]) - public_key_y = bytes_to_long(public_key_xy[key_len+1:]) - - return (ECC.construct(curve="P-224", d=private_key_d), - ECC.construct(curve="P-224", point_x=public_key_x, point_y=public_key_y)) - - -def create_ref_keys_p256(): - key_len = 32 - key_lines = load_file("ecc_p256.txt").splitlines() - private_key_d = bytes_to_long(compact(key_lines[2:5])) - public_key_xy = compact(key_lines[6:11]) - assert bord(public_key_xy[0]) == 4 # Uncompressed - public_key_x = bytes_to_long(public_key_xy[1:key_len+1]) - public_key_y = bytes_to_long(public_key_xy[key_len+1:]) - - return (ECC.construct(curve="P-256", d=private_key_d), - ECC.construct(curve="P-256", point_x=public_key_x, point_y=public_key_y)) - - -def create_ref_keys_p384(): - key_len = 48 - key_lines = load_file("ecc_p384.txt").splitlines() - private_key_d = bytes_to_long(compact(key_lines[2:6])) - public_key_xy = compact(key_lines[7:14]) - assert bord(public_key_xy[0]) == 4 # Uncompressed - public_key_x = bytes_to_long(public_key_xy[1:key_len+1]) - public_key_y = bytes_to_long(public_key_xy[key_len+1:]) - - return (ECC.construct(curve="P-384", d=private_key_d), - ECC.construct(curve="P-384", point_x=public_key_x, point_y=public_key_y)) - - -def create_ref_keys_p521(): - key_len = 66 - key_lines = load_file("ecc_p521.txt").splitlines() - private_key_d = bytes_to_long(compact(key_lines[2:7])) - public_key_xy = compact(key_lines[8:17]) - assert bord(public_key_xy[0]) == 4 # Uncompressed - public_key_x = bytes_to_long(public_key_xy[1:key_len+1]) - public_key_y = bytes_to_long(public_key_xy[key_len+1:]) - - return (ECC.construct(curve="P-521", d=private_key_d), - ECC.construct(curve="P-521", point_x=public_key_x, point_y=public_key_y)) - - -def create_ref_keys_ed25519(): - key_lines = load_file("ecc_ed25519.txt").splitlines() - seed = compact(key_lines[5:8]) - key = ECC.construct(curve="Ed25519", seed=seed) - return (key, key.public_key()) - - -def create_ref_keys_ed448(): - key_lines = load_file("ecc_ed448.txt").splitlines() - seed = compact(key_lines[6:10]) - key = ECC.construct(curve="Ed448", seed=seed) - return (key, key.public_key()) - - -# Create reference key pair -# ref_private, ref_public = create_ref_keys_p521() - -def get_fixed_prng(): - return SHAKE128.new().update(b"SEED").read - - -def extract_bitstring_from_spki(data): - seq = DerSequence() - seq.decode(data) - bs = DerBitString() - bs.decode(seq[1]) - return bs.value - - -class TestImport(unittest.TestCase): - - def test_empty(self): - self.assertRaises(ValueError, ECC.import_key, b"") - - def test_mismatch(self): - # The private key does not match the public key - mismatch = """-----BEGIN PRIVATE KEY----- -MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJChZANiAAQarFRaqflo -I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng -o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXk= ------END PRIVATE KEY-----""" - self.assertRaises(ValueError, ECC.import_key, mismatch) - - -class TestImport_P192(unittest.TestCase): - - def __init__(self, *args, **kwargs): - super(TestImport_P192, self).__init__(*args, **kwargs) - self.ref_private, self.ref_public = create_ref_keys_p192() - - def test_import_public_der(self): - key_file = load_file("ecc_p192_public.der") - - key = ECC._import_subjectPublicKeyInfo(key_file) - self.assertEqual(self.ref_public, key) - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_public, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_sec1_uncompressed(self): - key_file = load_file("ecc_p192_public.der") - value = extract_bitstring_from_spki(key_file) - key = ECC.import_key(key_file, curve_name='P192') - self.assertEqual(self.ref_public, key) - - def test_import_sec1_compressed(self): - key_file = load_file("ecc_p192_public_compressed.der") - value = extract_bitstring_from_spki(key_file) - key = ECC.import_key(key_file, curve_name='P192') - self.assertEqual(self.ref_public, key) - - def test_import_rfc5915_der(self): - key_file = load_file("ecc_p192_private.der") - - key = ECC._import_rfc5915_der(key_file, None) - self.assertEqual(self.ref_private, key) - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_clear(self): - key_file = load_file("ecc_p192_private_p8_clear.der") - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_in_pem_clear(self): - key_file = load_file("ecc_p192_private_p8_clear.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_1(self): - key_file = load_file("ecc_p192_private_p8.der") - - key = ECC._import_der(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_2(self): - key_file = load_file("ecc_p192_private_p8.pem") - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_x509_der(self): - key_file = load_file("ecc_p192_x509.der") - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_public, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_public_pem(self): - key_file = load_file("ecc_p192_public.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_private_pem(self): - key_file = load_file("ecc_p192_private.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pem_encrypted(self): - for algo in "des3", "aes128", "aes192", "aes256", "aes256_gcm": - key_file = load_file("ecc_p192_private_enc_%s.pem" % algo) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(tostr(key_file), b"secret") - self.assertEqual(self.ref_private, key) - - def test_import_x509_pem(self): - key_file = load_file("ecc_p192_x509.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - -class TestImport_P224(unittest.TestCase): - - def __init__(self, *args, **kwargs): - super(TestImport_P224, self).__init__(*args, **kwargs) - self.ref_private, self.ref_public = create_ref_keys_p224() - - def test_import_public_der(self): - key_file = load_file("ecc_p224_public.der") - - key = ECC._import_subjectPublicKeyInfo(key_file) - self.assertEqual(self.ref_public, key) - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_public, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_sec1_uncompressed(self): - key_file = load_file("ecc_p224_public.der") - value = extract_bitstring_from_spki(key_file) - key = ECC.import_key(key_file, curve_name='P224') - self.assertEqual(self.ref_public, key) - - def test_import_sec1_compressed(self): - key_file = load_file("ecc_p224_public_compressed.der") - value = extract_bitstring_from_spki(key_file) - key = ECC.import_key(key_file, curve_name='P224') - self.assertEqual(self.ref_public, key) - - def test_import_rfc5915_der(self): - key_file = load_file("ecc_p224_private.der") - - key = ECC._import_rfc5915_der(key_file, None) - self.assertEqual(self.ref_private, key) - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_clear(self): - key_file = load_file("ecc_p224_private_p8_clear.der") - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_in_pem_clear(self): - key_file = load_file("ecc_p224_private_p8_clear.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_1(self): - key_file = load_file("ecc_p224_private_p8.der") - - key = ECC._import_der(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_2(self): - key_file = load_file("ecc_p224_private_p8.pem") - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_3(self): - key_file = load_file("ecc_p224_private_p8_2.der") - - key = ECC._import_der(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_x509_der(self): - key_file = load_file("ecc_p224_x509.der") - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_public, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_public_pem(self): - key_file = load_file("ecc_p224_public.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_private_pem(self): - key_file = load_file("ecc_p224_private.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pem_encrypted(self): - for algo in "des3", "aes128", "aes192", "aes256", "aes256_gcm": - key_file = load_file("ecc_p224_private_enc_%s.pem" % algo) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(tostr(key_file), b"secret") - self.assertEqual(self.ref_private, key) - - def test_import_x509_pem(self): - key_file = load_file("ecc_p224_x509.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - -class TestImport_P256(unittest.TestCase): - - def __init__(self, *args, **kwargs): - super(TestImport_P256, self).__init__(*args, **kwargs) - self.ref_private, self.ref_public = create_ref_keys_p256() - - def test_import_public_der(self): - key_file = load_file("ecc_p256_public.der") - - key = ECC._import_subjectPublicKeyInfo(key_file) - self.assertEqual(self.ref_public, key) - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_public, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_sec1_uncompressed(self): - key_file = load_file("ecc_p256_public.der") - value = extract_bitstring_from_spki(key_file) - key = ECC.import_key(key_file, curve_name='P256') - self.assertEqual(self.ref_public, key) - - def test_import_sec1_compressed(self): - key_file = load_file("ecc_p256_public_compressed.der") - value = extract_bitstring_from_spki(key_file) - key = ECC.import_key(key_file, curve_name='P256') - self.assertEqual(self.ref_public, key) - - def test_import_rfc5915_der(self): - key_file = load_file("ecc_p256_private.der") - - key = ECC._import_rfc5915_der(key_file, None) - self.assertEqual(self.ref_private, key) - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_clear(self): - key_file = load_file("ecc_p256_private_p8_clear.der") - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_in_pem_clear(self): - key_file = load_file("ecc_p256_private_p8_clear.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_1(self): - key_file = load_file("ecc_p256_private_p8.der") - - key = ECC._import_der(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_2(self): - key_file = load_file("ecc_p256_private_p8.pem") - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_3(self): - key_file = load_file("ecc_p256_private_p8_2.der") - - key = ECC._import_der(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_x509_der(self): - key_file = load_file("ecc_p256_x509.der") - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_public, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_public_pem(self): - key_file = load_file("ecc_p256_public.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_private_pem(self): - key_file = load_file("ecc_p256_private.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pem_with_ecparams(self): - key_file = load_file("ecc_p256_private_ecparams.pem") - key = ECC.import_key(key_file) - # We just check if the import succeeds - - def test_import_private_pem_encrypted(self): - for algo in "des3", "aes128", "aes192", "aes256", "aes256_gcm": - key_file = load_file("ecc_p256_private_enc_%s.pem" % algo) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(tostr(key_file), b"secret") - self.assertEqual(self.ref_private, key) - - def test_import_x509_pem(self): - key_file = load_file("ecc_p256_x509.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_openssh_public(self): - key_file = load_file("ecc_p256_public_openssh.txt") - - key = ECC._import_openssh_public(key_file) - self.assertEqual(self.ref_public, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_openssh_private_clear(self): - key_file = load_file("ecc_p256_private_openssh.pem") - key_file_old = load_file("ecc_p256_private_openssh_old.pem") - - key = ECC.import_key(key_file) - key_old = ECC.import_key(key_file_old) - self.assertEqual(key, key_old) - - def test_import_openssh_private_password(self): - key_file = load_file("ecc_p256_private_openssh_pwd.pem") - key_file_old = load_file("ecc_p256_private_openssh_pwd_old.pem") - - key = ECC.import_key(key_file, b"password") - key_old = ECC.import_key(key_file_old) - self.assertEqual(key, key_old) - - -class TestImport_P384(unittest.TestCase): - - def __init__(self, *args, **kwargs): - super(TestImport_P384, self).__init__(*args, **kwargs) - self.ref_private, self.ref_public = create_ref_keys_p384() - - def test_import_public_der(self): - key_file = load_file("ecc_p384_public.der") - - key = ECC._import_subjectPublicKeyInfo(key_file) - self.assertEqual(self.ref_public, key) - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_public, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_sec1_uncompressed(self): - key_file = load_file("ecc_p384_public.der") - value = extract_bitstring_from_spki(key_file) - key = ECC.import_key(key_file, curve_name='P384') - self.assertEqual(self.ref_public, key) - - def test_import_sec1_compressed(self): - key_file = load_file("ecc_p384_public_compressed.der") - value = extract_bitstring_from_spki(key_file) - key = ECC.import_key(key_file, curve_name='P384') - self.assertEqual(self.ref_public, key) - - def test_import_rfc5915_der(self): - key_file = load_file("ecc_p384_private.der") - - key = ECC._import_rfc5915_der(key_file, None) - self.assertEqual(self.ref_private, key) - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_clear(self): - key_file = load_file("ecc_p384_private_p8_clear.der") - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_in_pem_clear(self): - key_file = load_file("ecc_p384_private_p8_clear.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_1(self): - key_file = load_file("ecc_p384_private_p8.der") - - key = ECC._import_der(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_2(self): - key_file = load_file("ecc_p384_private_p8.pem") - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_3(self): - key_file = load_file("ecc_p384_private_p8_2.der") - - key = ECC._import_der(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_x509_der(self): - key_file = load_file("ecc_p384_x509.der") - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_public, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_public_pem(self): - key_file = load_file("ecc_p384_public.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_private_pem(self): - key_file = load_file("ecc_p384_private.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pem_encrypted(self): - for algo in "des3", "aes128", "aes192", "aes256", "aes256_gcm": - key_file = load_file("ecc_p384_private_enc_%s.pem" % algo) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(tostr(key_file), b"secret") - self.assertEqual(self.ref_private, key) - - def test_import_x509_pem(self): - key_file = load_file("ecc_p384_x509.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_openssh_public(self): - key_file = load_file("ecc_p384_public_openssh.txt") - - key = ECC._import_openssh_public(key_file) - self.assertEqual(self.ref_public, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_openssh_private_clear(self): - key_file = load_file("ecc_p384_private_openssh.pem") - key_file_old = load_file("ecc_p384_private_openssh_old.pem") - - key = ECC.import_key(key_file) - key_old = ECC.import_key(key_file_old) - self.assertEqual(key, key_old) - - def test_import_openssh_private_password(self): - key_file = load_file("ecc_p384_private_openssh_pwd.pem") - key_file_old = load_file("ecc_p384_private_openssh_pwd_old.pem") - - key = ECC.import_key(key_file, b"password") - key_old = ECC.import_key(key_file_old) - self.assertEqual(key, key_old) - - -class TestImport_P521(unittest.TestCase): - - def __init__(self, *args, **kwargs): - super(TestImport_P521, self).__init__(*args, **kwargs) - self.ref_private, self.ref_public = create_ref_keys_p521() - - def test_import_public_der(self): - key_file = load_file("ecc_p521_public.der") - - key = ECC._import_subjectPublicKeyInfo(key_file) - self.assertEqual(self.ref_public, key) - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_public, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_sec1_uncompressed(self): - key_file = load_file("ecc_p521_public.der") - value = extract_bitstring_from_spki(key_file) - key = ECC.import_key(key_file, curve_name='P521') - self.assertEqual(self.ref_public, key) - - def test_import_sec1_compressed(self): - key_file = load_file("ecc_p521_public_compressed.der") - value = extract_bitstring_from_spki(key_file) - key = ECC.import_key(key_file, curve_name='P521') - self.assertEqual(self.ref_public, key) - - def test_import_rfc5915_der(self): - key_file = load_file("ecc_p521_private.der") - - key = ECC._import_rfc5915_der(key_file, None) - self.assertEqual(self.ref_private, key) - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_clear(self): - key_file = load_file("ecc_p521_private_p8_clear.der") - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_in_pem_clear(self): - key_file = load_file("ecc_p521_private_p8_clear.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_1(self): - key_file = load_file("ecc_p521_private_p8.der") - - key = ECC._import_der(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_2(self): - key_file = load_file("ecc_p521_private_p8.pem") - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_3(self): - key_file = load_file("ecc_p521_private_p8_2.der") - - key = ECC._import_der(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_x509_der(self): - key_file = load_file("ecc_p521_x509.der") - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_public, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_public_pem(self): - key_file = load_file("ecc_p521_public.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_private_pem(self): - key_file = load_file("ecc_p521_private.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pem_encrypted(self): - for algo in "des3", "aes128", "aes192", "aes256", "aes256_gcm": - key_file = load_file("ecc_p521_private_enc_%s.pem" % algo) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(tostr(key_file), b"secret") - self.assertEqual(self.ref_private, key) - - def test_import_x509_pem(self): - key_file = load_file("ecc_p521_x509.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_openssh_public(self): - key_file = load_file("ecc_p521_public_openssh.txt") - - key = ECC._import_openssh_public(key_file) - self.assertEqual(self.ref_public, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_openssh_private_clear(self): - key_file = load_file("ecc_p521_private_openssh.pem") - key_file_old = load_file("ecc_p521_private_openssh_old.pem") - - key = ECC.import_key(key_file) - key_old = ECC.import_key(key_file_old) - self.assertEqual(key, key_old) - - def test_import_openssh_private_password(self): - key_file = load_file("ecc_p521_private_openssh_pwd.pem") - key_file_old = load_file("ecc_p521_private_openssh_pwd_old.pem") - - key = ECC.import_key(key_file, b"password") - key_old = ECC.import_key(key_file_old) - self.assertEqual(key, key_old) - - -class TestExport_P192(unittest.TestCase): - - def __init__(self, *args, **kwargs): - super(TestExport_P192, self).__init__(*args, **kwargs) - self.ref_private, self.ref_public = create_ref_keys_p192() - - def test_export_public_der_uncompressed(self): - key_file = load_file("ecc_p192_public.der") - - encoded = self.ref_public._export_subjectPublicKeyInfo(False) - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="DER") - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="DER", compress=False) - self.assertEqual(key_file, encoded) - - def test_export_public_der_compressed(self): - key_file = load_file("ecc_p192_public.der") - pub_key = ECC.import_key(key_file) - key_file_compressed = pub_key.export_key(format="DER", compress=True) - - key_file_compressed_ref = load_file("ecc_p192_public_compressed.der") - self.assertEqual(key_file_compressed, key_file_compressed_ref) - - def test_export_public_sec1_uncompressed(self): - key_file = load_file("ecc_p192_public.der") - value = extract_bitstring_from_spki(key_file) - - encoded = self.ref_public.export_key(format="SEC1") - self.assertEqual(value, encoded) - - def test_export_public_sec1_compressed(self): - key_file = load_file("ecc_p192_public.der") - encoded = self.ref_public.export_key(format="SEC1", compress=True) - - key_file_compressed_ref = load_file("ecc_p192_public_compressed.der") - value = extract_bitstring_from_spki(key_file_compressed_ref) - self.assertEqual(value, encoded) - - def test_export_rfc5915_private_der(self): - key_file = load_file("ecc_p192_private.der") - - encoded = self.ref_private._export_rfc5915_private_der() - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="DER", use_pkcs8=False) - self.assertEqual(key_file, encoded) - - def test_export_private_pkcs8_clear(self): - key_file = load_file("ecc_p192_private_p8_clear.der") - - encoded = self.ref_private._export_pkcs8() - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="DER") - self.assertEqual(key_file, encoded) - - def test_export_private_pkcs8_encrypted(self): - encoded = self.ref_private._export_pkcs8(passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # This should prove that the output is password-protected - self.assertRaises(ValueError, ECC._import_pkcs8, encoded, None) - - decoded = ECC._import_pkcs8(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="DER", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="DER", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA224AndAES192-CBC", - prot_params={'iteration_count':123}) - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - def test_export_public_pem_uncompressed(self): - key_file = load_file("ecc_p192_public.pem", "rt").strip() - - encoded = self.ref_private._export_public_pem(False) - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_public.export_key(format="PEM") - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="PEM", compress=False) - self.assertEqual(key_file, encoded) - - def test_export_public_pem_compressed(self): - key_file = load_file("ecc_p192_public.pem", "rt").strip() - pub_key = ECC.import_key(key_file) - - key_file_compressed = pub_key.export_key(format="PEM", compress=True) - key_file_compressed_ref = load_file("ecc_p192_public_compressed.pem", "rt").strip() - - self.assertEqual(key_file_compressed, key_file_compressed_ref) - - def test_export_private_pem_clear(self): - key_file = load_file("ecc_p192_private.pem", "rt").strip() - - encoded = self.ref_private._export_private_pem(None) - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM", use_pkcs8=False) - self.assertEqual(key_file, encoded) - - def test_export_private_pem_encrypted(self): - encoded = self.ref_private._export_private_pem(passphrase=b"secret") - - # This should prove that the output is password-protected - self.assertRaises(ValueError, ECC.import_key, encoded) - - assert "EC PRIVATE KEY" in encoded - - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM", - passphrase="secret", - use_pkcs8=False) - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - def test_export_private_pkcs8_and_pem_1(self): - # PKCS8 inside PEM with both unencrypted - key_file = load_file("ecc_p192_private_p8_clear.pem", "rt").strip() - - encoded = self.ref_private._export_private_clear_pkcs8_in_clear_pem() - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM") - self.assertEqual(key_file, encoded) - - def test_export_private_pkcs8_and_pem_2(self): - # PKCS8 inside PEM with PKCS8 encryption - encoded = self.ref_private._export_private_encrypted_pkcs8_in_clear_pem("secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # This should prove that the output is password-protected - self.assertRaises(ValueError, ECC.import_key, encoded) - - assert "ENCRYPTED PRIVATE KEY" in encoded - - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - def test_prng(self): - # Test that password-protected containers use the provided PRNG - encoded1 = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", - randfunc=get_fixed_prng()) - encoded2 = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", - randfunc=get_fixed_prng()) - self.assertEqual(encoded1, encoded2) - - # --- - - encoded1 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase="secret", - randfunc=get_fixed_prng()) - encoded2 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase="secret", - randfunc=get_fixed_prng()) - self.assertEqual(encoded1, encoded2) - - def test_byte_or_string_passphrase(self): - encoded1 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase="secret", - randfunc=get_fixed_prng()) - encoded2 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase=b"secret", - randfunc=get_fixed_prng()) - self.assertEqual(encoded1, encoded2) - - def test_error_params1(self): - # Unknown format - self.assertRaises(ValueError, self.ref_private.export_key, format="XXX") - - # Missing 'protection' parameter when PKCS#8 is used - self.ref_private.export_key(format="PEM", passphrase="secret", - use_pkcs8=False) - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="secret") - - # DER format but no PKCS#8 - self.assertRaises(ValueError, self.ref_private.export_key, format="DER", - passphrase="secret", - use_pkcs8=False, - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # Incorrect parameters for public keys - self.assertRaises(ValueError, self.ref_public.export_key, format="DER", - use_pkcs8=False) - - # Empty password - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="", use_pkcs8=False) - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - def test_compressed_curve(self): - - # Compressed P-192 curve (Y-point is even) - pem1 = """-----BEGIN EC PRIVATE KEY----- - MF8CAQEEGHvhXmIW95JxZYfd4AUPu9BwknjuvS36aqAKBggqhkjOPQMBAaE0AzIA - BLJZCyTu35DQIlqvMlBynn3k1Ig+dWfg/brRhHecxptrbloqFSP8ITw0CwbGF+2X - 5g== - -----END EC PRIVATE KEY-----""" - - # Compressed P-192 curve (Y-point is odd) - pem2 = """-----BEGIN EC PRIVATE KEY----- - MF8CAQEEGA3rAotUaWl7d47eX6tz9JmLzOMJwl13XaAKBggqhkjOPQMBAaE0AzIA - BG4tHlTBBBGokcWmGm2xubVB0NvPC/Ou5AYwivs+3iCxmEjsymVAj6iiuX2Lxr6g - /Q== - -----END EC PRIVATE KEY-----""" - - key1 = ECC.import_key(pem1) - low16 = int(key1.pointQ.y % 65536) - self.assertEqual(low16, 0x97E6) - - key2 = ECC.import_key(pem2) - low16 = int(key2.pointQ.y % 65536) - self.assertEqual(low16, 0xA0FD) - - -class TestExport_P224(unittest.TestCase): - - def __init__(self, *args, **kwargs): - super(TestExport_P224, self).__init__(*args, **kwargs) - self.ref_private, self.ref_public = create_ref_keys_p224() - - def test_export_public_der_uncompressed(self): - key_file = load_file("ecc_p224_public.der") - - encoded = self.ref_public._export_subjectPublicKeyInfo(False) - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="DER") - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="DER", compress=False) - self.assertEqual(key_file, encoded) - - def test_export_public_der_compressed(self): - key_file = load_file("ecc_p224_public.der") - pub_key = ECC.import_key(key_file) - key_file_compressed = pub_key.export_key(format="DER", compress=True) - - key_file_compressed_ref = load_file("ecc_p224_public_compressed.der") - self.assertEqual(key_file_compressed, key_file_compressed_ref) - - def test_export_public_sec1_uncompressed(self): - key_file = load_file("ecc_p224_public.der") - value = extract_bitstring_from_spki(key_file) - - encoded = self.ref_public.export_key(format="SEC1") - self.assertEqual(value, encoded) - - def test_export_public_sec1_compressed(self): - key_file = load_file("ecc_p224_public.der") - encoded = self.ref_public.export_key(format="SEC1", compress=True) - - key_file_compressed_ref = load_file("ecc_p224_public_compressed.der") - value = extract_bitstring_from_spki(key_file_compressed_ref) - self.assertEqual(value, encoded) - - def test_export_rfc5915_private_der(self): - key_file = load_file("ecc_p224_private.der") - - encoded = self.ref_private._export_rfc5915_private_der() - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="DER", use_pkcs8=False) - self.assertEqual(key_file, encoded) - - def test_export_private_pkcs8_clear(self): - key_file = load_file("ecc_p224_private_p8_clear.der") - - encoded = self.ref_private._export_pkcs8() - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="DER") - self.assertEqual(key_file, encoded) - - def test_export_private_pkcs8_encrypted(self): - encoded = self.ref_private._export_pkcs8(passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # This should prove that the output is password-protected - self.assertRaises(ValueError, ECC._import_pkcs8, encoded, None) - - decoded = ECC._import_pkcs8(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="DER", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA512-224AndAES128-CBC", - prot_params={'iteration_count':123}) - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - def test_export_public_pem_uncompressed(self): - key_file = load_file("ecc_p224_public.pem", "rt").strip() - - encoded = self.ref_private._export_public_pem(False) - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_public.export_key(format="PEM") - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="PEM", compress=False) - self.assertEqual(key_file, encoded) - - def test_export_public_pem_compressed(self): - key_file = load_file("ecc_p224_public.pem", "rt").strip() - pub_key = ECC.import_key(key_file) - - key_file_compressed = pub_key.export_key(format="PEM", compress=True) - key_file_compressed_ref = load_file("ecc_p224_public_compressed.pem", "rt").strip() - - self.assertEqual(key_file_compressed, key_file_compressed_ref) - - def test_export_private_pem_clear(self): - key_file = load_file("ecc_p224_private.pem", "rt").strip() - - encoded = self.ref_private._export_private_pem(None) - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM", use_pkcs8=False) - self.assertEqual(key_file, encoded) - - def test_export_private_pem_encrypted(self): - encoded = self.ref_private._export_private_pem(passphrase=b"secret") - - # This should prove that the output is password-protected - self.assertRaises(ValueError, ECC.import_key, encoded) - - assert "EC PRIVATE KEY" in encoded - - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM", - passphrase="secret", - use_pkcs8=False) - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - def test_export_private_pkcs8_and_pem_1(self): - # PKCS8 inside PEM with both unencrypted - key_file = load_file("ecc_p224_private_p8_clear.pem", "rt").strip() - - encoded = self.ref_private._export_private_clear_pkcs8_in_clear_pem() - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM") - self.assertEqual(key_file, encoded) - - def test_export_private_pkcs8_and_pem_2(self): - # PKCS8 inside PEM with PKCS8 encryption - encoded = self.ref_private._export_private_encrypted_pkcs8_in_clear_pem("secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # This should prove that the output is password-protected - self.assertRaises(ValueError, ECC.import_key, encoded) - - assert "ENCRYPTED PRIVATE KEY" in encoded - - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - def test_prng(self): - # Test that password-protected containers use the provided PRNG - encoded1 = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", - randfunc=get_fixed_prng()) - encoded2 = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", - randfunc=get_fixed_prng()) - self.assertEqual(encoded1, encoded2) - - # --- - - encoded1 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase="secret", - randfunc=get_fixed_prng()) - encoded2 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase="secret", - randfunc=get_fixed_prng()) - self.assertEqual(encoded1, encoded2) - - def test_byte_or_string_passphrase(self): - encoded1 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase="secret", - randfunc=get_fixed_prng()) - encoded2 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase=b"secret", - randfunc=get_fixed_prng()) - self.assertEqual(encoded1, encoded2) - - def test_error_params1(self): - # Unknown format - self.assertRaises(ValueError, self.ref_private.export_key, format="XXX") - - # Missing 'protection' parameter when PKCS#8 is used - self.ref_private.export_key(format="PEM", passphrase="secret", - use_pkcs8=False) - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="secret") - - # DER format but no PKCS#8 - self.assertRaises(ValueError, self.ref_private.export_key, format="DER", - passphrase="secret", - use_pkcs8=False, - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # Incorrect parameters for public keys - self.assertRaises(ValueError, self.ref_public.export_key, format="DER", - use_pkcs8=False) - - # Empty password - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="", use_pkcs8=False) - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - def test_compressed_curve(self): - - # Compressed P-224 curve (Y-point is even) - pem1 = """-----BEGIN EC PRIVATE KEY----- - MGgCAQEEHPYicBNI9nd6wDKAX2l+f3A0Q+KWUQeMqSt5GoOgBwYFK4EEACGhPAM6 - AATCL6rUIDT14zXKoS5GQUMDP/tpc+1iI/FyEZikt2roKDkhU5q08srmqaysbfJN - eUr7Xf1lnCVGag== - -----END EC PRIVATE KEY-----""" - - # Compressed P-224 curve (Y-point is odd) - pem2 = """-----BEGIN EC PRIVATE KEY----- - MGgCAQEEHEFjbaVPLJ3ngZyCibCvT0RLUqSlHjC5Z3e0FtugBwYFK4EEACGhPAM6 - AAT5IvL2V6m48y1JLMGr6ZbnOqNKP9hMf9mxyVkk6/SaRoBoJVkXrNIpYL0P7DS7 - QF8E/OGeZRwvow== - -----END EC PRIVATE KEY-----""" - - key1 = ECC.import_key(pem1) - low16 = int(key1.pointQ.y % 65536) - self.assertEqual(low16, 0x466A) - - key2 = ECC.import_key(pem2) - low16 = int(key2.pointQ.y % 65536) - self.assertEqual(low16, 0x2FA3) - - -class TestExport_P256(unittest.TestCase): - - def __init__(self, *args, **kwargs): - super(TestExport_P256, self).__init__(*args, **kwargs) - self.ref_private, self.ref_public = create_ref_keys_p256() - - def test_export_public_der_uncompressed(self): - key_file = load_file("ecc_p256_public.der") - - encoded = self.ref_public._export_subjectPublicKeyInfo(False) - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="DER") - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="DER", compress=False) - self.assertEqual(key_file, encoded) - - def test_export_public_der_compressed(self): - key_file = load_file("ecc_p256_public.der") - pub_key = ECC.import_key(key_file) - key_file_compressed = pub_key.export_key(format="DER", compress=True) - - key_file_compressed_ref = load_file("ecc_p256_public_compressed.der") - self.assertEqual(key_file_compressed, key_file_compressed_ref) - - def test_export_public_sec1_uncompressed(self): - key_file = load_file("ecc_p256_public.der") - value = extract_bitstring_from_spki(key_file) - - encoded = self.ref_public.export_key(format="SEC1") - self.assertEqual(value, encoded) - - def test_export_public_sec1_compressed(self): - key_file = load_file("ecc_p256_public.der") - encoded = self.ref_public.export_key(format="SEC1", compress=True) - - key_file_compressed_ref = load_file("ecc_p256_public_compressed.der") - value = extract_bitstring_from_spki(key_file_compressed_ref) - self.assertEqual(value, encoded) - - def test_export_rfc5915_private_der(self): - key_file = load_file("ecc_p256_private.der") - - encoded = self.ref_private._export_rfc5915_private_der() - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="DER", use_pkcs8=False) - self.assertEqual(key_file, encoded) - - def test_export_private_pkcs8_clear(self): - key_file = load_file("ecc_p256_private_p8_clear.der") - - encoded = self.ref_private._export_pkcs8() - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="DER") - self.assertEqual(key_file, encoded) - - def test_export_private_pkcs8_encrypted(self): - encoded = self.ref_private._export_pkcs8(passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # This should prove that the output is password-protected - self.assertRaises(ValueError, ECC._import_pkcs8, encoded, None) - - decoded = ECC._import_pkcs8(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="DER", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="DER", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA512-256AndAES128-CBC", - prot_params={'iteration_count':123}) - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - def test_export_public_pem_uncompressed(self): - key_file = load_file("ecc_p256_public.pem", "rt").strip() - - encoded = self.ref_private._export_public_pem(False) - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_public.export_key(format="PEM") - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="PEM", compress=False) - self.assertEqual(key_file, encoded) - - def test_export_public_pem_compressed(self): - key_file = load_file("ecc_p256_public.pem", "rt").strip() - pub_key = ECC.import_key(key_file) - - key_file_compressed = pub_key.export_key(format="PEM", compress=True) - key_file_compressed_ref = load_file("ecc_p256_public_compressed.pem", "rt").strip() - - self.assertEqual(key_file_compressed, key_file_compressed_ref) - - def test_export_private_pem_clear(self): - key_file = load_file("ecc_p256_private.pem", "rt").strip() - - encoded = self.ref_private._export_private_pem(None) - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM", use_pkcs8=False) - self.assertEqual(key_file, encoded) - - def test_export_private_pem_encrypted(self): - encoded = self.ref_private._export_private_pem(passphrase=b"secret") - - # This should prove that the output is password-protected - self.assertRaises(ValueError, ECC.import_key, encoded) - - assert "EC PRIVATE KEY" in encoded - - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM", - passphrase="secret", - use_pkcs8=False) - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - def test_export_private_pkcs8_and_pem_1(self): - # PKCS8 inside PEM with both unencrypted - key_file = load_file("ecc_p256_private_p8_clear.pem", "rt").strip() - - encoded = self.ref_private._export_private_clear_pkcs8_in_clear_pem() - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM") - self.assertEqual(key_file, encoded) - - def test_export_private_pkcs8_and_pem_2(self): - # PKCS8 inside PEM with PKCS8 encryption - encoded = self.ref_private._export_private_encrypted_pkcs8_in_clear_pem("secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # This should prove that the output is password-protected - self.assertRaises(ValueError, ECC.import_key, encoded) - - assert "ENCRYPTED PRIVATE KEY" in encoded - - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - def test_export_openssh_uncompressed(self): - key_file = load_file("ecc_p256_public_openssh.txt", "rt") - - encoded = self.ref_public._export_openssh(False) - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_public.export_key(format="OpenSSH") - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="OpenSSH", compress=False) - self.assertEqual(key_file, encoded) - - def test_export_openssh_compressed(self): - key_file = load_file("ecc_p256_public_openssh.txt", "rt") - pub_key = ECC.import_key(key_file) - - key_file_compressed = pub_key.export_key(format="OpenSSH", compress=True) - assert len(key_file) > len(key_file_compressed) - self.assertEqual(pub_key, ECC.import_key(key_file_compressed)) - - def test_prng(self): - # Test that password-protected containers use the provided PRNG - encoded1 = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", - randfunc=get_fixed_prng()) - encoded2 = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", - randfunc=get_fixed_prng()) - self.assertEqual(encoded1, encoded2) - - # --- - - encoded1 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase="secret", - randfunc=get_fixed_prng()) - encoded2 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase="secret", - randfunc=get_fixed_prng()) - self.assertEqual(encoded1, encoded2) - - def test_byte_or_string_passphrase(self): - encoded1 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase="secret", - randfunc=get_fixed_prng()) - encoded2 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase=b"secret", - randfunc=get_fixed_prng()) - self.assertEqual(encoded1, encoded2) - - def test_error_params1(self): - # Unknown format - self.assertRaises(ValueError, self.ref_private.export_key, format="XXX") - - # Missing 'protection' parameter when PKCS#8 is used - self.ref_private.export_key(format="PEM", passphrase="secret", - use_pkcs8=False) - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="secret") - - # DER format but no PKCS#8 - self.assertRaises(ValueError, self.ref_private.export_key, format="DER", - passphrase="secret", - use_pkcs8=False, - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # Incorrect parameters for public keys - self.assertRaises(ValueError, self.ref_public.export_key, format="DER", - use_pkcs8=False) - - # Empty password - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="", use_pkcs8=False) - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # No private keys with OpenSSH - self.assertRaises(ValueError, self.ref_private.export_key, format="OpenSSH", - passphrase="secret") - - - def test_compressed_curve(self): - - # Compressed P-256 curve (Y-point is even) - pem1 = """-----BEGIN EC PRIVATE KEY----- - MFcCAQEEIHTuc09jC51xXomV6MVCDN+DpAAvSmaJWZPTEHM6D5H1oAoGCCqGSM49 - AwEHoSQDIgACWFuGbHe8yJ43rir7PMTE9w8vHz0BSpXHq90Xi7/s+a0= - -----END EC PRIVATE KEY-----""" - - # Compressed P-256 curve (Y-point is odd) - pem2 = """-----BEGIN EC PRIVATE KEY----- - MFcCAQEEIFggiPN9SQP+FAPTCPp08fRUz7rHp2qNBRcBJ1DXhb3ZoAoGCCqGSM49 - AwEHoSQDIgADLpph1trTIlVfa8NJvlMUPyWvL+wP+pW3BJITUL/wj9A= - -----END EC PRIVATE KEY-----""" - - key1 = ECC.import_key(pem1) - low16 = int(key1.pointQ.y % 65536) - self.assertEqual(low16, 0xA6FC) - - key2 = ECC.import_key(pem2) - low16 = int(key2.pointQ.y % 65536) - self.assertEqual(low16, 0x6E57) - - -class TestExport_P384(unittest.TestCase): - - def __init__(self, *args, **kwargs): - super(TestExport_P384, self).__init__(*args, **kwargs) - self.ref_private, self.ref_public = create_ref_keys_p384() - - def test_export_public_der_uncompressed(self): - key_file = load_file("ecc_p384_public.der") - - encoded = self.ref_public._export_subjectPublicKeyInfo(False) - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="DER") - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="DER", compress=False) - self.assertEqual(key_file, encoded) - - def test_export_public_der_compressed(self): - key_file = load_file("ecc_p384_public.der") - pub_key = ECC.import_key(key_file) - key_file_compressed = pub_key.export_key(format="DER", compress=True) - - key_file_compressed_ref = load_file("ecc_p384_public_compressed.der") - self.assertEqual(key_file_compressed, key_file_compressed_ref) - - def test_export_public_sec1_uncompressed(self): - key_file = load_file("ecc_p384_public.der") - value = extract_bitstring_from_spki(key_file) - - encoded = self.ref_public.export_key(format="SEC1") - self.assertEqual(value, encoded) - - def test_export_public_sec1_compressed(self): - key_file = load_file("ecc_p384_public.der") - encoded = self.ref_public.export_key(format="SEC1", compress=True) - - key_file_compressed_ref = load_file("ecc_p384_public_compressed.der") - value = extract_bitstring_from_spki(key_file_compressed_ref) - self.assertEqual(value, encoded) - - def test_export_rfc5915_private_der(self): - key_file = load_file("ecc_p384_private.der") - - encoded = self.ref_private._export_rfc5915_private_der() - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="DER", use_pkcs8=False) - self.assertEqual(key_file, encoded) - - def test_export_private_pkcs8_clear(self): - key_file = load_file("ecc_p384_private_p8_clear.der") - - encoded = self.ref_private._export_pkcs8() - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="DER") - self.assertEqual(key_file, encoded) - - def test_export_private_pkcs8_encrypted(self): - encoded = self.ref_private._export_pkcs8(passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # This should prove that the output is password-protected - self.assertRaises(ValueError, ECC._import_pkcs8, encoded, None) - - decoded = ECC._import_pkcs8(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="DER", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="DER", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA384AndAES128-CBC", - prot_params={'iteration_count':123}) - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - def test_export_public_pem_uncompressed(self): - key_file = load_file("ecc_p384_public.pem", "rt").strip() - - encoded = self.ref_private._export_public_pem(False) - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_public.export_key(format="PEM") - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="PEM", compress=False) - self.assertEqual(key_file, encoded) - - def test_export_public_pem_compressed(self): - key_file = load_file("ecc_p384_public.pem", "rt").strip() - pub_key = ECC.import_key(key_file) - - key_file_compressed = pub_key.export_key(format="PEM", compress=True) - key_file_compressed_ref = load_file("ecc_p384_public_compressed.pem", "rt").strip() - - self.assertEqual(key_file_compressed, key_file_compressed_ref) - - def test_export_private_pem_clear(self): - key_file = load_file("ecc_p384_private.pem", "rt").strip() - - encoded = self.ref_private._export_private_pem(None) - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM", use_pkcs8=False) - self.assertEqual(key_file, encoded) - - def test_export_private_pem_encrypted(self): - encoded = self.ref_private._export_private_pem(passphrase=b"secret") - - # This should prove that the output is password-protected - self.assertRaises(ValueError, ECC.import_key, encoded) - - assert "EC PRIVATE KEY" in encoded - - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM", - passphrase="secret", - use_pkcs8=False) - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - def test_export_private_pkcs8_and_pem_1(self): - # PKCS8 inside PEM with both unencrypted - key_file = load_file("ecc_p384_private_p8_clear.pem", "rt").strip() - - encoded = self.ref_private._export_private_clear_pkcs8_in_clear_pem() - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM") - self.assertEqual(key_file, encoded) - - def test_export_private_pkcs8_and_pem_2(self): - # PKCS8 inside PEM with PKCS8 encryption - encoded = self.ref_private._export_private_encrypted_pkcs8_in_clear_pem("secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # This should prove that the output is password-protected - self.assertRaises(ValueError, ECC.import_key, encoded) - - assert "ENCRYPTED PRIVATE KEY" in encoded - - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - def test_export_openssh_uncompressed(self): - key_file = load_file("ecc_p384_public_openssh.txt", "rt") - - encoded = self.ref_public._export_openssh(False) - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_public.export_key(format="OpenSSH") - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="OpenSSH", compress=False) - self.assertEqual(key_file, encoded) - - def test_export_openssh_compressed(self): - key_file = load_file("ecc_p384_public_openssh.txt", "rt") - pub_key = ECC.import_key(key_file) - - key_file_compressed = pub_key.export_key(format="OpenSSH", compress=True) - assert len(key_file) > len(key_file_compressed) - self.assertEqual(pub_key, ECC.import_key(key_file_compressed)) - - def test_prng(self): - # Test that password-protected containers use the provided PRNG - encoded1 = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", - randfunc=get_fixed_prng()) - encoded2 = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", - randfunc=get_fixed_prng()) - self.assertEqual(encoded1, encoded2) - - # --- - - encoded1 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase="secret", - randfunc=get_fixed_prng()) - encoded2 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase="secret", - randfunc=get_fixed_prng()) - self.assertEqual(encoded1, encoded2) - - def test_byte_or_string_passphrase(self): - encoded1 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase="secret", - randfunc=get_fixed_prng()) - encoded2 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase=b"secret", - randfunc=get_fixed_prng()) - self.assertEqual(encoded1, encoded2) - - def test_error_params1(self): - # Unknown format - self.assertRaises(ValueError, self.ref_private.export_key, format="XXX") - - # Missing 'protection' parameter when PKCS#8 is used - self.ref_private.export_key(format="PEM", passphrase="secret", - use_pkcs8=False) - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="secret") - - # DER format but no PKCS#8 - self.assertRaises(ValueError, self.ref_private.export_key, format="DER", - passphrase="secret", - use_pkcs8=False, - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # Incorrect parameters for public keys - self.assertRaises(ValueError, self.ref_public.export_key, format="DER", - use_pkcs8=False) - - # Empty password - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="", use_pkcs8=False) - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # No private keys with OpenSSH - self.assertRaises(ValueError, self.ref_private.export_key, format="OpenSSH", - passphrase="secret") - - def test_compressed_curve(self): - - # Compressed P-384 curve (Y-point is even) - # openssl ecparam -name secp384p1 -genkey -noout -conv_form compressed -out /tmp/a.pem - # openssl ec -in /tmp/a.pem -text -noout - pem1 = """-----BEGIN EC PRIVATE KEY----- -MIGkAgEBBDAM0lEIhvXuekK2SWtdbgOcZtBaxa9TxfpO/GcDFZLCJ3JVXaTgwken -QT+C+XLtD6WgBwYFK4EEACKhZANiAATs0kZMhFDu8DoBC21jrSDPyAUn4aXZ/DM4 -ylhDfWmb4LEbeszXceIzfhIUaaGs5y1xXaqf5KXTiAAYx2pKUzAAM9lcGUHCGKJG -k4AgUmVJON29XoUilcFrzjDmuye3B6Q= ------END EC PRIVATE KEY-----""" - - # Compressed P-384 curve (Y-point is odd) - pem2 = """-----BEGIN EC PRIVATE KEY----- -MIGkAgEBBDDHPFTslYLltE16fHdSDTtE/2HTmd3M8mqy5MttAm4wZ833KXiGS9oe -kFdx9sNV0KygBwYFK4EEACKhZANiAASLIE5RqVMtNhtBH/u/p/ifqOAlKnK/+RrQ -YC46ZRsnKNayw3wATdPjgja7L/DSII3nZK0G6KOOVwJBznT/e+zudUJYhZKaBLRx -/bgXyxUtYClOXxb1Y/5N7txLstYRyP0= ------END EC PRIVATE KEY-----""" - - key1 = ECC.import_key(pem1) - low16 = int(key1.pointQ.y % 65536) - self.assertEqual(low16, 0x07a4) - - key2 = ECC.import_key(pem2) - low16 = int(key2.pointQ.y % 65536) - self.assertEqual(low16, 0xc8fd) - - -class TestExport_P521(unittest.TestCase): - - def __init__(self, *args, **kwargs): - super(TestExport_P521, self).__init__(*args, **kwargs) - self.ref_private, self.ref_public = create_ref_keys_p521() - - def test_export_public_der_uncompressed(self): - key_file = load_file("ecc_p521_public.der") - - encoded = self.ref_public._export_subjectPublicKeyInfo(False) - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="DER") - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="DER", compress=False) - self.assertEqual(key_file, encoded) - - def test_export_public_der_compressed(self): - key_file = load_file("ecc_p521_public.der") - pub_key = ECC.import_key(key_file) - key_file_compressed = pub_key.export_key(format="DER", compress=True) - - key_file_compressed_ref = load_file("ecc_p521_public_compressed.der") - self.assertEqual(key_file_compressed, key_file_compressed_ref) - - def test_export_public_sec1_uncompressed(self): - key_file = load_file("ecc_p521_public.der") - value = extract_bitstring_from_spki(key_file) - - encoded = self.ref_public.export_key(format="SEC1") - self.assertEqual(value, encoded) - - encoded = self.ref_public.export_key(format="raw") - self.assertEqual(value, encoded) - - def test_export_public_sec1_compressed(self): - key_file = load_file("ecc_p521_public.der") - encoded = self.ref_public.export_key(format="SEC1", compress=True) - - key_file_compressed_ref = load_file("ecc_p521_public_compressed.der") - value = extract_bitstring_from_spki(key_file_compressed_ref) - self.assertEqual(value, encoded) - - encoded = self.ref_public.export_key(format="raw", compress=True) - self.assertEqual(value, encoded) - - def test_export_rfc5915_private_der(self): - key_file = load_file("ecc_p521_private.der") - - encoded = self.ref_private._export_rfc5915_private_der() - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="DER", use_pkcs8=False) - self.assertEqual(key_file, encoded) - - def test_export_private_pkcs8_clear(self): - key_file = load_file("ecc_p521_private_p8_clear.der") - - encoded = self.ref_private._export_pkcs8() - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="DER") - self.assertEqual(key_file, encoded) - - def test_export_private_pkcs8_encrypted(self): - encoded = self.ref_private._export_pkcs8(passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # This should prove that the output is password-protected - self.assertRaises(ValueError, ECC._import_pkcs8, encoded, None) - - decoded = ECC._import_pkcs8(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="DER", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="DER", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", - prot_params={'iteration_count':123}) - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - def test_export_public_pem_uncompressed(self): - key_file = load_file("ecc_p521_public.pem", "rt").strip() - - encoded = self.ref_private._export_public_pem(False) - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_public.export_key(format="PEM") - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="PEM", compress=False) - self.assertEqual(key_file, encoded) - - def test_export_public_pem_compressed(self): - key_file = load_file("ecc_p521_public.pem", "rt").strip() - pub_key = ECC.import_key(key_file) - - key_file_compressed = pub_key.export_key(format="PEM", compress=True) - key_file_compressed_ref = load_file("ecc_p521_public_compressed.pem", "rt").strip() - - self.assertEqual(key_file_compressed, key_file_compressed_ref) - - def test_export_private_pem_clear(self): - key_file = load_file("ecc_p521_private.pem", "rt").strip() - - encoded = self.ref_private._export_private_pem(None) - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM", use_pkcs8=False) - self.assertEqual(key_file, encoded) - - def test_export_private_pem_encrypted(self): - encoded = self.ref_private._export_private_pem(passphrase=b"secret") - - # This should prove that the output is password-protected - self.assertRaises(ValueError, ECC.import_key, encoded) - - assert "EC PRIVATE KEY" in encoded - - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM", - passphrase="secret", - use_pkcs8=False) - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - def test_export_private_pkcs8_and_pem_1(self): - # PKCS8 inside PEM with both unencrypted - key_file = load_file("ecc_p521_private_p8_clear.pem", "rt").strip() - - encoded = self.ref_private._export_private_clear_pkcs8_in_clear_pem() - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM") - self.assertEqual(key_file, encoded) - - def test_export_private_pkcs8_and_pem_2(self): - # PKCS8 inside PEM with PKCS8 encryption - encoded = self.ref_private._export_private_encrypted_pkcs8_in_clear_pem("secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # This should prove that the output is password-protected - self.assertRaises(ValueError, ECC.import_key, encoded) - - assert "ENCRYPTED PRIVATE KEY" in encoded - - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - def test_export_openssh_uncompressed(self): - key_file = load_file("ecc_p521_public_openssh.txt", "rt") - - encoded = self.ref_public._export_openssh(False) - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_public.export_key(format="OpenSSH") - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="OpenSSH", compress=False) - self.assertEqual(key_file, encoded) - - def test_export_openssh_compressed(self): - key_file = load_file("ecc_p521_public_openssh.txt", "rt") - pub_key = ECC.import_key(key_file) - - key_file_compressed = pub_key.export_key(format="OpenSSH", compress=True) - assert len(key_file) > len(key_file_compressed) - self.assertEqual(pub_key, ECC.import_key(key_file_compressed)) - - def test_prng(self): - # Test that password-protected containers use the provided PRNG - encoded1 = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", - randfunc=get_fixed_prng()) - encoded2 = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", - randfunc=get_fixed_prng()) - self.assertEqual(encoded1, encoded2) - - # --- - - encoded1 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase="secret", - randfunc=get_fixed_prng()) - encoded2 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase="secret", - randfunc=get_fixed_prng()) - self.assertEqual(encoded1, encoded2) - - def test_byte_or_string_passphrase(self): - encoded1 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase="secret", - randfunc=get_fixed_prng()) - encoded2 = self.ref_private.export_key(format="PEM", - use_pkcs8=False, - passphrase=b"secret", - randfunc=get_fixed_prng()) - self.assertEqual(encoded1, encoded2) - - def test_error_params1(self): - # Unknown format - self.assertRaises(ValueError, self.ref_private.export_key, format="XXX") - - # Missing 'protection' parameter when PKCS#8 is used - self.ref_private.export_key(format="PEM", passphrase="secret", - use_pkcs8=False) - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="secret") - - # DER format but no PKCS#8 - self.assertRaises(ValueError, self.ref_private.export_key, format="DER", - passphrase="secret", - use_pkcs8=False, - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # Incorrect parameters for public keys - self.assertRaises(ValueError, self.ref_public.export_key, format="DER", - use_pkcs8=False) - - # Empty password - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="", use_pkcs8=False) - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # No private keys with OpenSSH - self.assertRaises(ValueError, self.ref_private.export_key, format="OpenSSH", - passphrase="secret") - - def test_compressed_curve(self): - - # Compressed P-521 curve (Y-point is even) - # openssl ecparam -name secp521r1 -genkey -noout -conv_form compressed -out /tmp/a.pem - # openssl ec -in /tmp/a.pem -text -noout - pem1 = """-----BEGIN EC PRIVATE KEY----- -MIHcAgEBBEIAnm1CEjVjvNfXEN730p+D6su5l+mOztdc5XmTEoti+s2R4GQ4mAv3 -0zYLvyklvOHw0+yy8d0cyGEJGb8T3ZVKmg2gBwYFK4EEACOhgYkDgYYABAHzjTI1 -ckxQ3Togi0LAxiG0PucdBBBs5oIy3df95xv6SInp70z+4qQ2EltEmdNMssH8eOrl -M5CYdZ6nbcHMVaJUvQEzTrYxvFjOgJiOd+E9eBWbLkbMNqsh1UKVO6HbMbW0ohCI -uGxO8tM6r3w89/qzpG2SvFM/fvv3mIR30wSZDD84qA== ------END EC PRIVATE KEY-----""" - - # Compressed P-521 curve (Y-point is odd) - pem2 = """-----BEGIN EC PRIVATE KEY----- -MIHcAgEBBEIB84OfhJluLBRLn3+cC/RQ37C2SfQVP/t0gQK2tCsTf5avRcWYRrOJ -PmX9lNnkC0Hobd75QFRmdxrB0Wd1/M4jZOWgBwYFK4EEACOhgYkDgYYABAAMZcdJ -1YLCGHt3bHCEzdidVy6+brlJIbv1aQ9fPQLF7WKNv4c8w3H8d5a2+SDZilBOsk5c -6cNJDMz2ExWQvxl4CwDJtJGt1+LHVKFGy73NANqVxMbRu+2F8lOxkNp/ziFTbVyV -vv6oYkMIIi7r5oQWAiQDrR2mlrrFDL9V7GH/r8SWQw== ------END EC PRIVATE KEY-----""" - - key1 = ECC.import_key(pem1) - low16 = int(key1.pointQ.y % 65536) - self.assertEqual(low16, 0x38a8) - - key2 = ECC.import_key(pem2) - low16 = int(key2.pointQ.y % 65536) - self.assertEqual(low16, 0x9643) - - -class TestImport_Ed25519(unittest.TestCase): - - def __init__(self, *args, **kwargs): - super(TestImport_Ed25519, self).__init__(*args, **kwargs) - self.ref_private, self.ref_public = create_ref_keys_ed25519() - - def test_import_public_der(self): - key_file = load_file("ecc_ed25519_public.der") - - key = ECC._import_subjectPublicKeyInfo(key_file) - self.assertEqual(self.ref_public, key) - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_public, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_pkcs8_der(self): - key_file = load_file("ecc_ed25519_private.der") - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_1(self): - key_file = load_file("ecc_ed25519_private_p8.der") - - key = ECC._import_der(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_2(self): - key_file = load_file("ecc_ed25519_private_p8.pem") - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_3(self): - key_file = load_file("ecc_ed25519_private_p8_2.der") - - key = ECC._import_der(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_x509_der(self): - key_file = load_file("ecc_ed25519_x509.der") - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_public, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_public_pem(self): - key_file = load_file("ecc_ed25519_public.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_private_pem(self): - key_file = load_file("ecc_ed25519_private.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pem_encrypted(self): - for algo in "des3", "aes128", "aes192", "aes256": - key_file = load_file("ecc_ed25519_private_enc_%s.pem" % algo) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(tostr(key_file), b"secret") - self.assertEqual(self.ref_private, key) - - def test_import_x509_pem(self): - key_file = load_file("ecc_ed25519_x509.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_openssh_public(self): - key_file = load_file("ecc_ed25519_public_openssh.txt") - key = ECC._import_openssh_public(key_file) - self.assertFalse(key.has_private()) - key = ECC.import_key(key_file) - self.assertFalse(key.has_private()) - - def test_import_openssh_private_clear(self): - key_file = load_file("ecc_ed25519_private_openssh.pem") - key = ECC.import_key(key_file) - - def test_import_openssh_private_password(self): - key_file = load_file("ecc_ed25519_private_openssh_pwd.pem") - key = ECC.import_key(key_file, b"password") - - -class TestExport_Ed25519(unittest.TestCase): - - def __init__(self, *args, **kwargs): - super(TestExport_Ed25519, self).__init__(*args, **kwargs) - self.ref_private, self.ref_public = create_ref_keys_ed25519() - - def test_export_public_der(self): - key_file = load_file("ecc_ed25519_public.der") - - encoded = self.ref_public._export_subjectPublicKeyInfo(True) - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="DER") - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="DER", compress=False) - self.assertEqual(key_file, encoded) - - def test_export_public_sec1(self): - self.assertRaises(ValueError, self.ref_public.export_key, format="SEC1") - - def test_export_private_pkcs8_clear(self): - key_file = load_file("ecc_ed25519_private.der") - - encoded = self.ref_private._export_pkcs8() - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="DER") - self.assertEqual(key_file, encoded) - - self.assertRaises(ValueError, self.ref_private.export_key, - format="DER", use_pkcs8=False) - - def test_export_private_pkcs8_encrypted(self): - encoded = self.ref_private._export_pkcs8(passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # This should prove that the output is password-protected - self.assertRaises(ValueError, ECC._import_pkcs8, encoded, None) - - decoded = ECC._import_pkcs8(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="DER", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="DER", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA256AndAES128-CBC", - prot_params={'iteration_count':123}) - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - def test_export_public_pem(self): - key_file_ref = load_file("ecc_ed25519_public.pem", "rt").strip() - key_file = self.ref_public.export_key(format="PEM").strip() - self.assertEqual(key_file_ref, key_file) - - def test_export_private_pem_clear(self): - key_file = load_file("ecc_ed25519_private.pem", "rt").strip() - encoded = self.ref_private.export_key(format="PEM").strip() - self.assertEqual(key_file, encoded) - - def test_export_private_pem_encrypted(self): - encoded = self.ref_private.export_key(format="PEM", - passphrase=b"secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # This should prove that the output is password-protected - self.assertRaises(ValueError, ECC.import_key, encoded) - - assert "ENCRYPTED PRIVATE KEY" in encoded - - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - def test_export_openssh(self): - key_file = load_file("ecc_ed25519_public_openssh.txt", "rt") - public_key = ECC.import_key(key_file) - key_file = " ".join(key_file.split(' ')[:2]) # remove comment - - encoded = public_key._export_openssh(False) - self.assertEqual(key_file, encoded.strip()) - - encoded = public_key.export_key(format="OpenSSH") - self.assertEqual(key_file, encoded.strip()) - - def test_export_raw(self): - encoded = self.ref_public.export_key(format='raw') - self.assertEqual(encoded, unhexlify(b'bc85b8cf585d20a4de47e84d1cb6183f63d9ba96223fcbc886e363ffdea20cff')) - - def test_prng(self): - # Test that password-protected containers use the provided PRNG - encoded1 = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", - randfunc=get_fixed_prng()) - encoded2 = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", - randfunc=get_fixed_prng()) - self.assertEqual(encoded1, encoded2) - - def test_byte_or_string_passphrase(self): - encoded1 = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", - randfunc=get_fixed_prng()) - encoded2 = self.ref_private.export_key(format="PEM", - passphrase=b"secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", - randfunc=get_fixed_prng()) - self.assertEqual(encoded1, encoded2) - - def test_error_params1(self): - # Unknown format - self.assertRaises(ValueError, self.ref_private.export_key, format="XXX") - - # Missing 'protection' parameter when PKCS#8 is used - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="secret") - - # Empty password - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="", use_pkcs8=False) - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # No private keys with OpenSSH - self.assertRaises(ValueError, self.ref_private.export_key, format="OpenSSH", - passphrase="secret") - - -class TestImport_Ed448(unittest.TestCase): - - def __init__(self, *args, **kwargs): - super(TestImport_Ed448, self).__init__(*args, **kwargs) - self.ref_private, self.ref_public = create_ref_keys_ed448() - - def test_import_public_der(self): - key_file = load_file("ecc_ed448_public.der") - - key = ECC._import_subjectPublicKeyInfo(key_file) - self.assertEqual(self.ref_public, key) - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_public, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_pkcs8_der(self): - key_file = load_file("ecc_ed448_private.der") - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_1(self): - key_file = load_file("ecc_ed448_private_p8.der") - - key = ECC._import_der(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_2(self): - key_file = load_file("ecc_ed448_private_p8.pem") - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_private_pkcs8_encrypted_3(self): - key_file = load_file("ecc_ed448_private_p8_2.der") - - key = ECC._import_der(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - def test_import_x509_der(self): - key_file = load_file("ecc_ed448_x509.der") - - key = ECC._import_der(key_file, None) - self.assertEqual(self.ref_public, key) - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_public_pem(self): - key_file = load_file("ecc_ed448_public.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - def test_import_private_pem(self): - key_file = load_file("ecc_ed448_private.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_private, key) - - def test_import_private_pem_encrypted(self): - for algo in "des3", "aes128", "aes192", "aes256": - key_file = load_file("ecc_ed448_private_enc_%s.pem" % algo) - - key = ECC.import_key(key_file, "secret") - self.assertEqual(self.ref_private, key) - - key = ECC.import_key(tostr(key_file), b"secret") - self.assertEqual(self.ref_private, key) - - def test_import_x509_pem(self): - key_file = load_file("ecc_ed448_x509.pem") - - key = ECC.import_key(key_file) - self.assertEqual(self.ref_public, key) - - -class TestExport_Ed448(unittest.TestCase): - - def __init__(self, *args, **kwargs): - super(TestExport_Ed448, self).__init__(*args, **kwargs) - self.ref_private, self.ref_public = create_ref_keys_ed448() - - def test_export_public_der(self): - key_file = load_file("ecc_ed448_public.der") - - encoded = self.ref_public._export_subjectPublicKeyInfo(True) - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="DER") - self.assertEqual(key_file, encoded) - - encoded = self.ref_public.export_key(format="DER", compress=False) - self.assertEqual(key_file, encoded) - - def test_export_public_sec1(self): - self.assertRaises(ValueError, self.ref_public.export_key, format="SEC1") - - def test_export_private_pkcs8_clear(self): - key_file = load_file("ecc_ed448_private.der") - - encoded = self.ref_private._export_pkcs8() - self.assertEqual(key_file, encoded) - - # --- - - encoded = self.ref_private.export_key(format="DER") - self.assertEqual(key_file, encoded) - - self.assertRaises(ValueError, self.ref_private.export_key, - format="DER", use_pkcs8=False) - - def test_export_private_pkcs8_encrypted(self): - encoded = self.ref_private._export_pkcs8(passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # This should prove that the output is password-protected - self.assertRaises(ValueError, ECC._import_pkcs8, encoded, None) - - decoded = ECC._import_pkcs8(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="DER", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - # --- - - encoded = self.ref_private.export_key(format="DER", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA384AndAES128-CBC", - prot_params={'iteration_count':123}) - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - - def test_export_public_pem(self): - key_file_ref = load_file("ecc_ed448_public.pem", "rt").strip() - key_file = self.ref_public.export_key(format="PEM").strip() - self.assertEqual(key_file_ref, key_file) - - def test_export_private_pem_clear(self): - key_file = load_file("ecc_ed448_private.pem", "rt").strip() - encoded = self.ref_private.export_key(format="PEM").strip() - self.assertEqual(key_file, encoded) - - def test_export_private_pem_encrypted(self): - encoded = self.ref_private.export_key(format="PEM", - passphrase=b"secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # This should prove that the output is password-protected - self.assertRaises(ValueError, ECC.import_key, encoded) - - assert "ENCRYPTED PRIVATE KEY" in encoded - - decoded = ECC.import_key(encoded, "secret") - self.assertEqual(self.ref_private, decoded) - - def test_export_openssh(self): - # Not supported - self.assertRaises(ValueError, self.ref_public.export_key, format="OpenSSH") - - def test_export_raw(self): - encoded = self.ref_public.export_key(format='raw') - self.assertEqual(encoded, unhexlify(b'899014ddc0a0e1260cfc1085afdf952019e9fd63372e3e366e26dad32b176624884330a14617237e3081febd9d1a15069e7499433d2f55dd80')) - - def test_prng(self): - # Test that password-protected containers use the provided PRNG - encoded1 = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", - randfunc=get_fixed_prng()) - encoded2 = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", - randfunc=get_fixed_prng()) - self.assertEqual(encoded1, encoded2) - - def test_byte_or_string_passphrase(self): - encoded1 = self.ref_private.export_key(format="PEM", - passphrase="secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", - randfunc=get_fixed_prng()) - encoded2 = self.ref_private.export_key(format="PEM", - passphrase=b"secret", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC", - randfunc=get_fixed_prng()) - self.assertEqual(encoded1, encoded2) - - def test_error_params1(self): - # Unknown format - self.assertRaises(ValueError, self.ref_private.export_key, format="XXX") - - # Missing 'protection' parameter when PKCS#8 is used - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="secret") - - # Empty password - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="", use_pkcs8=False) - self.assertRaises(ValueError, self.ref_private.export_key, format="PEM", - passphrase="", - protection="PBKDF2WithHMAC-SHA1AndAES128-CBC") - - # No private keys with OpenSSH - self.assertRaises(ValueError, self.ref_private.export_key, format="OpenSSH", - passphrase="secret") - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(TestImport) - try: - tests += list_test_cases(TestImport_P192) - tests += list_test_cases(TestImport_P224) - tests += list_test_cases(TestImport_P256) - tests += list_test_cases(TestImport_P384) - tests += list_test_cases(TestImport_P521) - tests += list_test_cases(TestImport_Ed25519) - tests += list_test_cases(TestImport_Ed448) - - tests += list_test_cases(TestExport_P192) - tests += list_test_cases(TestExport_P224) - tests += list_test_cases(TestExport_P256) - tests += list_test_cases(TestExport_P384) - tests += list_test_cases(TestExport_P521) - tests += list_test_cases(TestExport_Ed25519) - tests += list_test_cases(TestExport_Ed448) - - except MissingTestVectorException: - pass - return tests - - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_import_RSA.py b/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_import_RSA.py deleted file mode 100644 index 01cf899..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/PublicKey/test_import_RSA.py +++ /dev/null @@ -1,629 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/PublicKey/test_importKey.py: Self-test for importing RSA keys -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -import os -import re -import errno -import warnings -import unittest - -from Cryptodome.PublicKey import RSA -from Cryptodome.SelfTest.st_common import a2b_hex, list_test_cases -from Cryptodome.IO import PEM -from Cryptodome.Util.py3compat import b, tostr, FileNotFoundError -from Cryptodome.Util.number import inverse, bytes_to_long -from Cryptodome.Util import asn1 - -try: - import pycryptodome_test_vectors # type: ignore - test_vectors_available = True -except ImportError: - test_vectors_available = False - - -def load_file(file_name, mode="rb"): - results = None - - try: - if not test_vectors_available: - raise FileNotFoundError(errno.ENOENT, - os.strerror(errno.ENOENT), - file_name) - - dir_comps = ("PublicKey", "RSA") - init_dir = os.path.dirname(pycryptodome_test_vectors.__file__) - full_file_name = os.path.join(os.path.join(init_dir, *dir_comps), file_name) - with open(full_file_name, mode) as file_in: - results = file_in.read() - - except FileNotFoundError: - warnings.warn("Warning: skipping extended tests for RSA", - UserWarning, - stacklevel=2) - - return results - - -def der2pem(der, text='PUBLIC'): - import binascii - chunks = [binascii.b2a_base64(der[i:i+48]) for i in range(0, len(der), 48)] - pem = b('-----BEGIN %s KEY-----\n' % text) - pem += b('').join(chunks) - pem += b('-----END %s KEY-----' % text) - return pem - - -class ImportKeyTests(unittest.TestCase): - # 512-bit RSA key generated with openssl - rsaKeyPEM = u'''-----BEGIN RSA PRIVATE KEY----- -MIIBOwIBAAJBAL8eJ5AKoIsjURpcEoGubZMxLD7+kT+TLr7UkvEtFrRhDDKMtuII -q19FrL4pUIMymPMSLBn3hJLe30Dw48GQM4UCAwEAAQJACUSDEp8RTe32ftq8IwG8 -Wojl5mAd1wFiIOrZ/Uv8b963WJOJiuQcVN29vxU5+My9GPZ7RA3hrDBEAoHUDPrI -OQIhAPIPLz4dphiD9imAkivY31Rc5AfHJiQRA7XixTcjEkojAiEAyh/pJHks/Mlr -+rdPNEpotBjfV4M4BkgGAA/ipcmaAjcCIQCHvhwwKVBLzzTscT2HeUdEeBMoiXXK -JACAr3sJQJGxIQIgarRp+m1WSKV1MciwMaTOnbU7wxFs9DP1pva76lYBzgUCIQC9 -n0CnZCJ6IZYqSt0H5N7+Q+2Ro64nuwV/OSQfM6sBwQ== ------END RSA PRIVATE KEY-----''' - - # As above, but this is actually an unencrypted PKCS#8 key - rsaKeyPEM8 = u'''-----BEGIN PRIVATE KEY----- -MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAvx4nkAqgiyNRGlwS -ga5tkzEsPv6RP5MuvtSS8S0WtGEMMoy24girX0WsvilQgzKY8xIsGfeEkt7fQPDj -wZAzhQIDAQABAkAJRIMSnxFN7fZ+2rwjAbxaiOXmYB3XAWIg6tn9S/xv3rdYk4mK -5BxU3b2/FTn4zL0Y9ntEDeGsMEQCgdQM+sg5AiEA8g8vPh2mGIP2KYCSK9jfVFzk -B8cmJBEDteLFNyMSSiMCIQDKH+kkeSz8yWv6t080Smi0GN9XgzgGSAYAD+KlyZoC -NwIhAIe+HDApUEvPNOxxPYd5R0R4EyiJdcokAICvewlAkbEhAiBqtGn6bVZIpXUx -yLAxpM6dtTvDEWz0M/Wm9rvqVgHOBQIhAL2fQKdkInohlipK3Qfk3v5D7ZGjrie7 -BX85JB8zqwHB ------END PRIVATE KEY-----''' - - # The same RSA private key as in rsaKeyPEM, but now encrypted - rsaKeyEncryptedPEM = ( - - # PEM encryption - # With DES and passphrase 'test' - ('test', u'''-----BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: DES-CBC,AF8F9A40BD2FA2FC - -Ckl9ex1kaVEWhYC2QBmfaF+YPiR4NFkRXA7nj3dcnuFEzBnY5XULupqQpQI3qbfA -u8GYS7+b3toWWiHZivHbAAUBPDIZG9hKDyB9Sq2VMARGsX1yW1zhNvZLIiVJzUHs -C6NxQ1IJWOXzTew/xM2I26kPwHIvadq+/VaT8gLQdjdH0jOiVNaevjWnLgrn1mLP -BCNRMdcexozWtAFNNqSzfW58MJL2OdMi21ED184EFytIc1BlB+FZiGZduwKGuaKy -9bMbdb/1PSvsSzPsqW7KSSrTw6MgJAFJg6lzIYvR5F4poTVBxwBX3+EyEmShiaNY -IRX3TgQI0IjrVuLmvlZKbGWP18FXj7I7k9tSsNOOzllTTdq3ny5vgM3A+ynfAaxp -dysKznQ6P+IoqML1WxAID4aGRMWka+uArOJ148Rbj9s= ------END RSA PRIVATE KEY-----'''), - - # PKCS8 encryption - ('winter', u'''-----BEGIN ENCRYPTED PRIVATE KEY----- -MIIBpjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIeZIsbW3O+JcCAggA -MBQGCCqGSIb3DQMHBAgSM2p0D8FilgSCAWBhFyP2tiGKVpGj3mO8qIBzinU60ApR -3unvP+N6j7LVgnV2lFGaXbJ6a1PbQXe+2D6DUyBLo8EMXrKKVLqOMGkFMHc0UaV6 -R6MmrsRDrbOqdpTuVRW+NVd5J9kQQh4xnfU/QrcPPt7vpJvSf4GzG0n666Ki50OV -M/feuVlIiyGXY6UWdVDpcOV72cq02eNUs/1JWdh2uEBvA9fCL0c07RnMrdT+CbJQ -NjJ7f8ULtp7xvR9O3Al/yJ4Wv3i4VxF1f3MCXzhlUD4I0ONlr0kJWgeQ80q/cWhw -ntvgJwnCn2XR1h6LA8Wp+0ghDTsL2NhJpWd78zClGhyU4r3hqu1XDjoXa7YCXCix -jCV15+ViDJzlNCwg+W6lRg18sSLkCT7alviIE0U5tHc6UPbbHwT5QqAxAABaP+nZ -CGqJGyiwBzrKebjgSm/KRd4C91XqcsysyH2kKPfT51MLAoD4xelOURBP ------END ENCRYPTED PRIVATE KEY-----''' - ), - ) - - rsaPublicKeyPEM = u'''-----BEGIN PUBLIC KEY----- -MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL8eJ5AKoIsjURpcEoGubZMxLD7+kT+T -Lr7UkvEtFrRhDDKMtuIIq19FrL4pUIMymPMSLBn3hJLe30Dw48GQM4UCAwEAAQ== ------END PUBLIC KEY-----''' - - # Obtained using 'ssh-keygen -i -m PKCS8 -f rsaPublicKeyPEM' - rsaPublicKeyOpenSSH = b('''ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAQQC/HieQCqCLI1EaXBKBrm2TMSw+/pE/ky6+1JLxLRa0YQwyjLbiCKtfRay+KVCDMpjzEiwZ94SS3t9A8OPBkDOF comment\n''') - - # The private key, in PKCS#1 format encoded with DER - rsaKeyDER = a2b_hex( - '''3082013b020100024100bf1e27900aa08b23511a5c1281ae6d93312c3efe - 913f932ebed492f12d16b4610c328cb6e208ab5f45acbe2950833298f312 - 2c19f78492dedf40f0e3c190338502030100010240094483129f114dedf6 - 7edabc2301bc5a88e5e6601dd7016220ead9fd4bfc6fdeb75893898ae41c - 54ddbdbf1539f8ccbd18f67b440de1ac30440281d40cfac839022100f20f - 2f3e1da61883f62980922bd8df545ce407c726241103b5e2c53723124a23 - 022100ca1fe924792cfcc96bfab74f344a68b418df578338064806000fe2 - a5c99a023702210087be1c3029504bcf34ec713d877947447813288975ca - 240080af7b094091b12102206ab469fa6d5648a57531c8b031a4ce9db53b - c3116cf433f5a6f6bbea5601ce05022100bd9f40a764227a21962a4add07 - e4defe43ed91a3ae27bb057f39241f33ab01c1 - '''.replace(" ","")) - - # The private key, in unencrypted PKCS#8 format encoded with DER - rsaKeyDER8 = a2b_hex( - '''30820155020100300d06092a864886f70d01010105000482013f3082013 - b020100024100bf1e27900aa08b23511a5c1281ae6d93312c3efe913f932 - ebed492f12d16b4610c328cb6e208ab5f45acbe2950833298f3122c19f78 - 492dedf40f0e3c190338502030100010240094483129f114dedf67edabc2 - 301bc5a88e5e6601dd7016220ead9fd4bfc6fdeb75893898ae41c54ddbdb - f1539f8ccbd18f67b440de1ac30440281d40cfac839022100f20f2f3e1da - 61883f62980922bd8df545ce407c726241103b5e2c53723124a23022100c - a1fe924792cfcc96bfab74f344a68b418df578338064806000fe2a5c99a0 - 23702210087be1c3029504bcf34ec713d877947447813288975ca240080a - f7b094091b12102206ab469fa6d5648a57531c8b031a4ce9db53bc3116cf - 433f5a6f6bbea5601ce05022100bd9f40a764227a21962a4add07e4defe4 - 3ed91a3ae27bb057f39241f33ab01c1 - '''.replace(" ","")) - - rsaPublicKeyDER = a2b_hex( - '''305c300d06092a864886f70d0101010500034b003048024100bf1e27900a - a08b23511a5c1281ae6d93312c3efe913f932ebed492f12d16b4610c328c - b6e208ab5f45acbe2950833298f3122c19f78492dedf40f0e3c190338502 - 03010001 - '''.replace(" ","")) - - n = int('BF 1E 27 90 0A A0 8B 23 51 1A 5C 12 81 AE 6D 93 31 2C 3E FE 91 3F 93 2E BE D4 92 F1 2D 16 B4 61 0C 32 8C B6 E2 08 AB 5F 45 AC BE 29 50 83 32 98 F3 12 2C 19 F7 84 92 DE DF 40 F0 E3 C1 90 33 85'.replace(" ",""),16) - e = 65537 - d = int('09 44 83 12 9F 11 4D ED F6 7E DA BC 23 01 BC 5A 88 E5 E6 60 1D D7 01 62 20 EA D9 FD 4B FC 6F DE B7 58 93 89 8A E4 1C 54 DD BD BF 15 39 F8 CC BD 18 F6 7B 44 0D E1 AC 30 44 02 81 D4 0C FA C8 39'.replace(" ",""),16) - p = int('00 F2 0F 2F 3E 1D A6 18 83 F6 29 80 92 2B D8 DF 54 5C E4 07 C7 26 24 11 03 B5 E2 C5 37 23 12 4A 23'.replace(" ",""),16) - q = int('00 CA 1F E9 24 79 2C FC C9 6B FA B7 4F 34 4A 68 B4 18 DF 57 83 38 06 48 06 00 0F E2 A5 C9 9A 02 37'.replace(" ",""),16) - - # This is q^{-1} mod p). fastmath and slowmath use pInv (p^{-1} - # mod q) instead! - qInv = int('00 BD 9F 40 A7 64 22 7A 21 96 2A 4A DD 07 E4 DE FE 43 ED 91 A3 AE 27 BB 05 7F 39 24 1F 33 AB 01 C1'.replace(" ",""),16) - pInv = inverse(p,q) - - def testImportKey1(self): - """Verify import of RSAPrivateKey DER SEQUENCE""" - key = RSA.importKey(self.rsaKeyDER) - self.assertTrue(key.has_private()) - self.assertEqual(key.n, self.n) - self.assertEqual(key.e, self.e) - self.assertEqual(key.d, self.d) - self.assertEqual(key.p, self.p) - self.assertEqual(key.q, self.q) - - def testImportKey2(self): - """Verify import of SubjectPublicKeyInfo DER SEQUENCE""" - key = RSA.importKey(self.rsaPublicKeyDER) - self.assertFalse(key.has_private()) - self.assertEqual(key.n, self.n) - self.assertEqual(key.e, self.e) - - def testImportKey3unicode(self): - """Verify import of RSAPrivateKey DER SEQUENCE, encoded with PEM as unicode""" - key = RSA.importKey(self.rsaKeyPEM) - self.assertEqual(key.has_private(),True) # assert_ - self.assertEqual(key.n, self.n) - self.assertEqual(key.e, self.e) - self.assertEqual(key.d, self.d) - self.assertEqual(key.p, self.p) - self.assertEqual(key.q, self.q) - - def testImportKey3bytes(self): - """Verify import of RSAPrivateKey DER SEQUENCE, encoded with PEM as byte string""" - key = RSA.importKey(b(self.rsaKeyPEM)) - self.assertEqual(key.has_private(),True) # assert_ - self.assertEqual(key.n, self.n) - self.assertEqual(key.e, self.e) - self.assertEqual(key.d, self.d) - self.assertEqual(key.p, self.p) - self.assertEqual(key.q, self.q) - - def testImportKey4unicode(self): - """Verify import of RSAPrivateKey DER SEQUENCE, encoded with PEM as unicode""" - key = RSA.importKey(self.rsaPublicKeyPEM) - self.assertEqual(key.has_private(),False) # assertFalse - self.assertEqual(key.n, self.n) - self.assertEqual(key.e, self.e) - - def testImportKey4bytes(self): - """Verify import of SubjectPublicKeyInfo DER SEQUENCE, encoded with PEM as byte string""" - key = RSA.importKey(b(self.rsaPublicKeyPEM)) - self.assertEqual(key.has_private(),False) # assertFalse - self.assertEqual(key.n, self.n) - self.assertEqual(key.e, self.e) - - def testImportKey5(self): - """Verifies that the imported key is still a valid RSA pair""" - key = RSA.importKey(self.rsaKeyPEM) - idem = key._encrypt(key._decrypt(89)) - self.assertEqual(idem, 89) - - def testImportKey6(self): - """Verifies that the imported key is still a valid RSA pair""" - key = RSA.importKey(self.rsaKeyDER) - idem = key._encrypt(key._decrypt(65)) - self.assertEqual(idem, 65) - - def testImportKey7(self): - """Verify import of OpenSSH public key""" - key = RSA.importKey(self.rsaPublicKeyOpenSSH) - self.assertEqual(key.n, self.n) - self.assertEqual(key.e, self.e) - - def testImportKey8(self): - """Verify import of encrypted PrivateKeyInfo DER SEQUENCE""" - for t in self.rsaKeyEncryptedPEM: - key = RSA.importKey(t[1], t[0]) - self.assertTrue(key.has_private()) - self.assertEqual(key.n, self.n) - self.assertEqual(key.e, self.e) - self.assertEqual(key.d, self.d) - self.assertEqual(key.p, self.p) - self.assertEqual(key.q, self.q) - - def testImportKey9(self): - """Verify import of unencrypted PrivateKeyInfo DER SEQUENCE""" - key = RSA.importKey(self.rsaKeyDER8) - self.assertTrue(key.has_private()) - self.assertEqual(key.n, self.n) - self.assertEqual(key.e, self.e) - self.assertEqual(key.d, self.d) - self.assertEqual(key.p, self.p) - self.assertEqual(key.q, self.q) - - def testImportKey10(self): - """Verify import of unencrypted PrivateKeyInfo DER SEQUENCE, encoded with PEM""" - key = RSA.importKey(self.rsaKeyPEM8) - self.assertTrue(key.has_private()) - self.assertEqual(key.n, self.n) - self.assertEqual(key.e, self.e) - self.assertEqual(key.d, self.d) - self.assertEqual(key.p, self.p) - self.assertEqual(key.q, self.q) - - def testImportKey11(self): - """Verify import of RSAPublicKey DER SEQUENCE""" - der = asn1.DerSequence([17, 3]).encode() - key = RSA.importKey(der) - self.assertEqual(key.n, 17) - self.assertEqual(key.e, 3) - - def testImportKey12(self): - """Verify import of RSAPublicKey DER SEQUENCE, encoded with PEM""" - der = asn1.DerSequence([17, 3]).encode() - pem = der2pem(der) - key = RSA.importKey(pem) - self.assertEqual(key.n, 17) - self.assertEqual(key.e, 3) - - def test_import_key_windows_cr_lf(self): - pem_cr_lf = "\r\n".join(self.rsaKeyPEM.splitlines()) - key = RSA.importKey(pem_cr_lf) - self.assertEqual(key.n, self.n) - self.assertEqual(key.e, self.e) - self.assertEqual(key.d, self.d) - self.assertEqual(key.p, self.p) - self.assertEqual(key.q, self.q) - - def test_import_empty(self): - self.assertRaises(ValueError, RSA.import_key, b"") - - ### - def testExportKey1(self): - key = RSA.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) - derKey = key.export_key("DER") - self.assertEqual(derKey, self.rsaKeyDER) - - def testExportKey2(self): - key = RSA.construct([self.n, self.e]) - derKey = key.export_key("DER") - self.assertEqual(derKey, self.rsaPublicKeyDER) - - def testExportKey3(self): - key = RSA.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) - pemKey = key.export_key("PEM") - self.assertEqual(pemKey, b(self.rsaKeyPEM)) - - def testExportKey4(self): - key = RSA.construct([self.n, self.e]) - pemKey = key.export_key("PEM") - self.assertEqual(pemKey, b(self.rsaPublicKeyPEM)) - - def testExportKey5(self): - key = RSA.construct([self.n, self.e]) - openssh_1 = key.export_key("OpenSSH").split() - openssh_2 = self.rsaPublicKeyOpenSSH.split() - self.assertEqual(openssh_1[0], openssh_2[0]) - self.assertEqual(openssh_1[1], openssh_2[1]) - - def testExportKey7(self): - key = RSA.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) - derKey = key.export_key("DER", pkcs=8) - self.assertEqual(derKey, self.rsaKeyDER8) - - def testExportKey8(self): - key = RSA.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) - pemKey = key.export_key("PEM", pkcs=8) - self.assertEqual(pemKey, b(self.rsaKeyPEM8)) - - def testExportKey9(self): - key = RSA.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) - self.assertRaises(ValueError, key.export_key, "invalid-format") - - def testExportKey10(self): - # Export and re-import the encrypted key. It must match. - # PEM envelope, PKCS#1, old PEM encryption - key = RSA.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) - outkey = key.export_key('PEM', 'test') - self.assertTrue(tostr(outkey).find('4,ENCRYPTED')!=-1) - self.assertTrue(tostr(outkey).find('BEGIN RSA PRIVATE KEY')!=-1) - inkey = RSA.importKey(outkey, 'test') - self.assertEqual(key.n, inkey.n) - self.assertEqual(key.e, inkey.e) - self.assertEqual(key.d, inkey.d) - - def testExportKey11(self): - # Export and re-import the encrypted key. It must match. - # PEM envelope, PKCS#1, old PEM encryption - key = RSA.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) - outkey = key.export_key('PEM', 'test', pkcs=1) - self.assertTrue(tostr(outkey).find('4,ENCRYPTED')!=-1) - self.assertTrue(tostr(outkey).find('BEGIN RSA PRIVATE KEY')!=-1) - inkey = RSA.importKey(outkey, 'test') - self.assertEqual(key.n, inkey.n) - self.assertEqual(key.e, inkey.e) - self.assertEqual(key.d, inkey.d) - - def testExportKey12(self): - # Export and re-import the encrypted key. It must match. - # PEM envelope, PKCS#8, old PEM encryption - key = RSA.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) - outkey = key.export_key('PEM', 'test', pkcs=8) - self.assertTrue(tostr(outkey).find('4,ENCRYPTED')!=-1) - self.assertTrue(tostr(outkey).find('BEGIN PRIVATE KEY')!=-1) - inkey = RSA.importKey(outkey, 'test') - self.assertEqual(key.n, inkey.n) - self.assertEqual(key.e, inkey.e) - self.assertEqual(key.d, inkey.d) - - def testExportKey13(self): - # Export and re-import the encrypted key. It must match. - # PEM envelope, PKCS#8, PKCS#8 encryption - key = RSA.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) - outkey = key.export_key('PEM', 'test', pkcs=8, - protection='PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC') - self.assertTrue(tostr(outkey).find('4,ENCRYPTED')==-1) - self.assertTrue(tostr(outkey).find('BEGIN ENCRYPTED PRIVATE KEY')!=-1) - inkey = RSA.importKey(outkey, 'test') - self.assertEqual(key.n, inkey.n) - self.assertEqual(key.e, inkey.e) - self.assertEqual(key.d, inkey.d) - - def testExportKey14(self): - # Export and re-import the encrypted key. It must match. - # DER envelope, PKCS#8, PKCS#8 encryption - key = RSA.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) - outkey = key.export_key('DER', 'test', pkcs=8) - inkey = RSA.importKey(outkey, 'test') - self.assertEqual(key.n, inkey.n) - self.assertEqual(key.e, inkey.e) - self.assertEqual(key.d, inkey.d) - - def testExportKey15(self): - # Verify that that error an condition is detected when trying to - # use a password with DER encoding and PKCS#1. - key = RSA.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) - self.assertRaises(ValueError, key.export_key, 'DER', 'test', 1) - - def testExportKey16(self): - # Export and re-import the encrypted key. It must match. - # PEM envelope, PKCS#8, PKCS#8 encryption with parameters - key = RSA.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) - outkey = key.export_key('PEM', 'test', pkcs=8, - protection='PBKDF2WithHMAC-SHA512AndAES256-CBC', - prot_params={'iteration_count':123} - ) - self.assertTrue(tostr(outkey).find('4,ENCRYPTED')==-1) - self.assertTrue(tostr(outkey).find('BEGIN ENCRYPTED PRIVATE KEY')!=-1) - - # Verify the iteration count - der = PEM.decode(tostr(outkey))[0] - seq1 = asn1.DerSequence().decode(der) - seq2 = asn1.DerSequence().decode(seq1[0]) - seq3 = asn1.DerSequence().decode(seq2[1]) - seq4 = asn1.DerSequence().decode(seq3[0]) - seq5 = asn1.DerSequence().decode(seq4[1]) - self.assertEqual(seq5[1], 123) - - inkey = RSA.importKey(outkey, 'test') - self.assertEqual(key.n, inkey.n) - self.assertEqual(key.e, inkey.e) - self.assertEqual(key.d, inkey.d) - - def test_import_key(self): - """Verify that import_key is an alias to importKey""" - key = RSA.import_key(self.rsaPublicKeyDER) - self.assertFalse(key.has_private()) - self.assertEqual(key.n, self.n) - self.assertEqual(key.e, self.e) - - def test_import_key_ba_mv(self): - """Verify that import_key can be used on bytearrays and memoryviews""" - key = RSA.import_key(bytearray(self.rsaPublicKeyDER)) - key = RSA.import_key(memoryview(self.rsaPublicKeyDER)) - - def test_exportKey(self): - key = RSA.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) - self.assertEqual(key.export_key(), key.exportKey()) - - -class ImportKeyFromX509Cert(unittest.TestCase): - - def test_x509v1(self): - - # Sample V1 certificate with a 1024 bit RSA key - x509_v1_cert = """ ------BEGIN CERTIFICATE----- -MIICOjCCAaMCAQEwDQYJKoZIhvcNAQEEBQAwfjENMAsGA1UEChMEQWNtZTELMAkG -A1UECxMCUkQxHDAaBgkqhkiG9w0BCQEWDXNwYW1AYWNtZS5vcmcxEzARBgNVBAcT -Ck1ldHJvcG9saXMxETAPBgNVBAgTCE5ldyBZb3JrMQswCQYDVQQGEwJVUzENMAsG -A1UEAxMEdGVzdDAeFw0xNDA3MTExOTU3MjRaFw0xNzA0MDYxOTU3MjRaME0xCzAJ -BgNVBAYTAlVTMREwDwYDVQQIEwhOZXcgWW9yazENMAsGA1UEChMEQWNtZTELMAkG -A1UECxMCUkQxDzANBgNVBAMTBmxhdHZpYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw -gYkCgYEAyG+kytdRj3TFbRmHDYp3TXugVQ81chew0qeOxZWOz80IjtWpgdOaCvKW -NCuc8wUR9BWrEQW+39SaRMLiQfQtyFSQZijc3nsEBu/Lo4uWZ0W/FHDRVSvkJA/V -Ex5NL5ikI+wbUeCV5KajGNDalZ8F1pk32+CBs8h1xNx5DyxuEHUCAwEAATANBgkq -hkiG9w0BAQQFAAOBgQCVQF9Y//Q4Psy+umEM38pIlbZ2hxC5xNz/MbVPwuCkNcGn -KYNpQJP+JyVTsPpO8RLZsAQDzRueMI3S7fbbwTzAflN0z19wvblvu93xkaBytVok -9VBAH28olVhy9b1MMeg2WOt5sUEQaFNPnwwsyiY9+HsRpvpRnPSQF+kyYVsshQ== ------END CERTIFICATE----- - """.strip() - - # RSA public key as dumped by openssl - exponent = 65537 - modulus_str = """ -00:c8:6f:a4:ca:d7:51:8f:74:c5:6d:19:87:0d:8a: -77:4d:7b:a0:55:0f:35:72:17:b0:d2:a7:8e:c5:95: -8e:cf:cd:08:8e:d5:a9:81:d3:9a:0a:f2:96:34:2b: -9c:f3:05:11:f4:15:ab:11:05:be:df:d4:9a:44:c2: -e2:41:f4:2d:c8:54:90:66:28:dc:de:7b:04:06:ef: -cb:a3:8b:96:67:45:bf:14:70:d1:55:2b:e4:24:0f: -d5:13:1e:4d:2f:98:a4:23:ec:1b:51:e0:95:e4:a6: -a3:18:d0:da:95:9f:05:d6:99:37:db:e0:81:b3:c8: -75:c4:dc:79:0f:2c:6e:10:75 - """ - modulus = int(re.sub("[^0-9a-f]","", modulus_str), 16) - - key = RSA.importKey(x509_v1_cert) - self.assertEqual(key.e, exponent) - self.assertEqual(key.n, modulus) - self.assertFalse(key.has_private()) - - def test_x509v3(self): - - # Sample V3 certificate with a 1024 bit RSA key - x509_v3_cert = """ ------BEGIN CERTIFICATE----- -MIIEcjCCAlqgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJVUzEL -MAkGA1UECAwCTUQxEjAQBgNVBAcMCUJhbHRpbW9yZTEQMA4GA1UEAwwHVGVzdCBD -QTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbTAeFw0xNDA3MTIwOTM1 -MTJaFw0xNzA0MDcwOTM1MTJaMEQxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNRDES -MBAGA1UEBwwJQmFsdGltb3JlMRQwEgYDVQQDDAtUZXN0IFNlcnZlcjCBnzANBgkq -hkiG9w0BAQEFAAOBjQAwgYkCgYEA/S7GJV2OcFdyNMQ4K75KrYFtMEn3VnEFdPHa -jyS37XlMxSh0oS4GeTGVUCJInl5Cpsv8WQdh03FfeOdvzp5IZ46OcjeOPiWnmjgl -2G5j7e2bDH7RSchGV+OD6Fb1Agvuu2/9iy8fdf3rPQ/7eAddzKUrzwacVbnW+tg2 -QtSXKRcCAwEAAaOB1TCB0jAdBgNVHQ4EFgQU/WwCX7FfWMIPDFfJ+I8a2COG+l8w -HwYDVR0jBBgwFoAUa0hkif3RMaraiWtsOOZZlLu9wJwwCQYDVR0TBAIwADALBgNV -HQ8EBAMCBeAwSgYDVR0RBEMwQYILZXhhbXBsZS5jb22CD3d3dy5leGFtcGxlLmNv -bYIQbWFpbC5leGFtcGxlLmNvbYIPZnRwLmV4YW1wbGUuY29tMCwGCWCGSAGG+EIB -DQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQsF -AAOCAgEAvO6xfdsGbnoK4My3eJthodTAjMjPwFVY133LH04QLcCv54TxKhtUg1fi -PgdjVe1HpTytPBfXy2bSZbXAN0abZCtw1rYrnn7o1g2pN8iypVq3zVn0iMTzQzxs -zEPO3bpR/UhNSf90PmCsS5rqZpAAnXSaAy1ClwHWk/0eG2pYkhE1m1ABVMN2lsAW -e9WxGk6IFqaI9O37NYQwmEypMs4DC+ECJEvbPFiqi3n0gbXCZJJ6omDA5xJldaYK -Oa7KR3s/qjBsu9UAiWpLBuFoSTHIF2aeRKRFmUdmzwo43eVPep65pY6eQ4AdL2RF -rqEuINbGlzI5oQyYhu71IwB+iPZXaZZPlwjLgOsuad/p2hOgDb5WxUi8FnDPursQ -ujfpIpmrOP/zpvvQWnwePI3lI+5n41kTBSbefXEdv6rXpHk3QRzB90uPxnXPdxSC -16ASA8bQT5an/1AgoE3k9CrcD2K0EmgaX0YI0HUhkyzbkg34EhpWJ6vvRUbRiNRo -9cIbt/ya9Y9u0Ja8GLXv6dwX0l0IdJMkL8KifXUFAVCujp1FBrr/gdmwQn8itANy -+qbnWSxmOvtaY0zcaFAcONuHva0h51/WqXOMO1eb8PhR4HIIYU8p1oBwQp7dSni8 -THDi1F+GG5PsymMDj5cWK42f+QzjVw5PrVmFqqrrEoMlx8DWh5Y= ------END CERTIFICATE----- -""".strip() - - # RSA public key as dumped by openssl - exponent = 65537 - modulus_str = """ -00:fd:2e:c6:25:5d:8e:70:57:72:34:c4:38:2b:be: -4a:ad:81:6d:30:49:f7:56:71:05:74:f1:da:8f:24: -b7:ed:79:4c:c5:28:74:a1:2e:06:79:31:95:50:22: -48:9e:5e:42:a6:cb:fc:59:07:61:d3:71:5f:78:e7: -6f:ce:9e:48:67:8e:8e:72:37:8e:3e:25:a7:9a:38: -25:d8:6e:63:ed:ed:9b:0c:7e:d1:49:c8:46:57:e3: -83:e8:56:f5:02:0b:ee:bb:6f:fd:8b:2f:1f:75:fd: -eb:3d:0f:fb:78:07:5d:cc:a5:2b:cf:06:9c:55:b9: -d6:fa:d8:36:42:d4:97:29:17 - """ - modulus = int(re.sub("[^0-9a-f]","", modulus_str), 16) - - key = RSA.importKey(x509_v3_cert) - self.assertEqual(key.e, exponent) - self.assertEqual(key.n, modulus) - self.assertFalse(key.has_private()) - - -class TestImport_2048(unittest.TestCase): - - def test_import_openssh_public(self): - key_file_ref = load_file("rsa2048_private.pem") - key_file = load_file("rsa2048_public_openssh.txt") - - # Skip test if test vectors are not installed - if None in (key_file_ref, key_file): - return - - key_ref = RSA.import_key(key_file_ref).public_key() - key = RSA.import_key(key_file) - self.assertEqual(key_ref, key) - - def test_import_openssh_private_clear(self): - key_file = load_file("rsa2048_private_openssh.pem") - key_file_old = load_file("rsa2048_private_openssh_old.pem") - - # Skip test if test vectors are not installed - if None in (key_file_old, key_file): - return - - key = RSA.import_key(key_file) - key_old = RSA.import_key(key_file_old) - - self.assertEqual(key, key_old) - - def test_import_openssh_private_password(self): - key_file = load_file("rsa2048_private_openssh_pwd.pem") - key_file_old = load_file("rsa2048_private_openssh_pwd_old.pem") - - # Skip test if test vectors are not installed - if None in (key_file_old, key_file): - return - - key = RSA.import_key(key_file, b"password") - key_old = RSA.import_key(key_file_old) - self.assertEqual(key, key_old) - - def test_import_pkcs8_private(self): - key_file_ref = load_file("rsa2048_private.pem") - key_file = load_file("rsa2048_private_p8.der") - - # Skip test if test vectors are not installed - if None in (key_file_ref, key_file): - return - - key_ref = RSA.import_key(key_file_ref) - key = RSA.import_key(key_file, b'secret') - self.assertEqual(key_ref, key) - - - -if __name__ == '__main__': - unittest.main() - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(ImportKeyTests) - tests += list_test_cases(ImportKeyFromX509Cert) - tests += list_test_cases(TestImport_2048) - return tests - - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Random/__init__.py b/resources/lib/deps/Cryptodome/SelfTest/Random/__init__.py deleted file mode 100644 index 763ee9c..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Random/__init__.py +++ /dev/null @@ -1,39 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Random/__init__.py: Self-test for random number generation modules -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test for random number generators""" - -__revision__ = "$Id$" - -def get_tests(config={}): - tests = [] - from Cryptodome.SelfTest.Random import test_random; tests += test_random.get_tests(config=config) - return tests - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Random/test_random.py b/resources/lib/deps/Cryptodome/SelfTest/Random/test_random.py deleted file mode 100644 index 30e9194..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Random/test_random.py +++ /dev/null @@ -1,167 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Util/test_generic.py: Self-test for the Cryptodome.Random.new() function -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Cryptodome.Random.new()""" - -import sys -import unittest -from Cryptodome.Util.py3compat import b - -class SimpleTest(unittest.TestCase): - def runTest(self): - """Cryptodome.Random.new()""" - # Import the Random module and try to use it - from Cryptodome import Random - randobj = Random.new() - x = randobj.read(16) - y = randobj.read(16) - self.assertNotEqual(x, y) - z = Random.get_random_bytes(16) - self.assertNotEqual(x, z) - self.assertNotEqual(y, z) - # Test the Random.random module, which - # implements a subset of Python's random API - # Not implemented: - # seed(), getstate(), setstate(), jumpahead() - # random(), uniform(), triangular(), betavariate() - # expovariate(), gammavariate(), gauss(), - # longnormvariate(), normalvariate(), - # vonmisesvariate(), paretovariate() - # weibullvariate() - # WichmannHill(), whseed(), SystemRandom() - from Cryptodome.Random import random - x = random.getrandbits(16*8) - y = random.getrandbits(16*8) - self.assertNotEqual(x, y) - # Test randrange - if x>y: - start = y - stop = x - else: - start = x - stop = y - for step in range(1,10): - x = random.randrange(start,stop,step) - y = random.randrange(start,stop,step) - self.assertNotEqual(x, y) - self.assertEqual(start <= x < stop, True) - self.assertEqual(start <= y < stop, True) - self.assertEqual((x - start) % step, 0) - self.assertEqual((y - start) % step, 0) - for i in range(10): - self.assertEqual(random.randrange(1,2), 1) - self.assertRaises(ValueError, random.randrange, start, start) - self.assertRaises(ValueError, random.randrange, stop, start, step) - self.assertRaises(TypeError, random.randrange, start, stop, step, step) - self.assertRaises(TypeError, random.randrange, start, stop, "1") - self.assertRaises(TypeError, random.randrange, "1", stop, step) - self.assertRaises(TypeError, random.randrange, 1, "2", step) - self.assertRaises(ValueError, random.randrange, start, stop, 0) - # Test randint - x = random.randint(start,stop) - y = random.randint(start,stop) - self.assertNotEqual(x, y) - self.assertEqual(start <= x <= stop, True) - self.assertEqual(start <= y <= stop, True) - for i in range(10): - self.assertEqual(random.randint(1,1), 1) - self.assertRaises(ValueError, random.randint, stop, start) - self.assertRaises(TypeError, random.randint, start, stop, step) - self.assertRaises(TypeError, random.randint, "1", stop) - self.assertRaises(TypeError, random.randint, 1, "2") - # Test choice - seq = range(10000) - x = random.choice(seq) - y = random.choice(seq) - self.assertNotEqual(x, y) - self.assertEqual(x in seq, True) - self.assertEqual(y in seq, True) - for i in range(10): - self.assertEqual(random.choice((1,2,3)) in (1,2,3), True) - self.assertEqual(random.choice([1,2,3]) in [1,2,3], True) - if sys.version_info[0] == 3: - self.assertEqual(random.choice(bytearray(b('123'))) in bytearray(b('123')), True) - self.assertEqual(1, random.choice([1])) - self.assertRaises(IndexError, random.choice, []) - self.assertRaises(TypeError, random.choice, 1) - # Test shuffle. Lacks random parameter to specify function. - # Make copies of seq - seq = range(500) - x = list(seq) - y = list(seq) - random.shuffle(x) - random.shuffle(y) - self.assertNotEqual(x, y) - self.assertEqual(len(seq), len(x)) - self.assertEqual(len(seq), len(y)) - for i in range(len(seq)): - self.assertEqual(x[i] in seq, True) - self.assertEqual(y[i] in seq, True) - self.assertEqual(seq[i] in x, True) - self.assertEqual(seq[i] in y, True) - z = [1] - random.shuffle(z) - self.assertEqual(z, [1]) - if sys.version_info[0] == 3: - z = bytearray(b('12')) - random.shuffle(z) - self.assertEqual(b('1') in z, True) - self.assertRaises(TypeError, random.shuffle, b('12')) - self.assertRaises(TypeError, random.shuffle, 1) - self.assertRaises(TypeError, random.shuffle, "11") - self.assertRaises(TypeError, random.shuffle, (1,2)) - # 2to3 wraps a list() around it, alas - but I want to shoot - # myself in the foot here! :D - # if sys.version_info[0] == 3: - # self.assertRaises(TypeError, random.shuffle, range(3)) - # Test sample - x = random.sample(seq, 20) - y = random.sample(seq, 20) - self.assertNotEqual(x, y) - for i in range(20): - self.assertEqual(x[i] in seq, True) - self.assertEqual(y[i] in seq, True) - z = random.sample([1], 1) - self.assertEqual(z, [1]) - z = random.sample((1,2,3), 1) - self.assertEqual(z[0] in (1,2,3), True) - z = random.sample("123", 1) - self.assertEqual(z[0] in "123", True) - z = random.sample(range(3), 1) - self.assertEqual(z[0] in range(3), True) - if sys.version_info[0] == 3: - z = random.sample(b("123"), 1) - self.assertEqual(z[0] in b("123"), True) - z = random.sample(bytearray(b("123")), 1) - self.assertEqual(z[0] in bytearray(b("123")), True) - self.assertRaises(TypeError, random.sample, 1) - -def get_tests(config={}): - return [SimpleTest()] - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Signature/__init__.py b/resources/lib/deps/Cryptodome/SelfTest/Signature/__init__.py deleted file mode 100644 index 83cf0f3..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Signature/__init__.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Signature/__init__.py: Self-test for signature modules -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test for signature modules""" - -import unittest -from . import test_pkcs1_15, test_pss, test_dss, test_eddsa - - -def get_tests(config={}): - tests = [] - tests += test_pkcs1_15.get_tests(config=config) - tests += test_pss.get_tests(config=config) - tests += test_dss.get_tests(config=config) - tests += test_eddsa.get_tests(config=config) - return tests - - -if __name__ == '__main__': - def suite(): - return unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Signature/test_dss.py b/resources/lib/deps/Cryptodome/SelfTest/Signature/test_dss.py deleted file mode 100644 index 156ee67..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Signature/test_dss.py +++ /dev/null @@ -1,1369 +0,0 @@ -# -# SelfTest/Signature/test_dss.py: Self-test for DSS signatures -# -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import re -import unittest -from binascii import hexlify, unhexlify - -from Cryptodome.Util.py3compat import tobytes, bord, bchr - -from Cryptodome.Hash import (SHA1, SHA224, SHA256, SHA384, SHA512, - SHA3_224, SHA3_256, SHA3_384, SHA3_512) -from Cryptodome.Signature import DSS -from Cryptodome.PublicKey import DSA, ECC -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.SelfTest.loader import load_test_vectors, load_test_vectors_wycheproof -from Cryptodome.Util.number import bytes_to_long, long_to_bytes - - -def t2b(hexstring): - ws = hexstring.replace(" ", "").replace("\n", "") - return unhexlify(tobytes(ws)) - - -def t2l(hexstring): - ws = hexstring.replace(" ", "").replace("\n", "") - return int(ws, 16) - - -def load_hash_by_name(hash_name): - return __import__("Cryptodome.Hash." + hash_name, globals(), locals(), ["new"]) - - -class StrRNG: - - def __init__(self, randomness): - length = len(randomness) - self._idx = 0 - # Fix required to get the right K (see how randint() works!) - self._randomness = long_to_bytes(bytes_to_long(randomness) - 1, length) - - def __call__(self, n): - out = self._randomness[self._idx:self._idx + n] - self._idx += n - return out - - -class FIPS_DSA_Tests(unittest.TestCase): - - # 1st 1024 bit key from SigGen.txt - P = 0xa8f9cd201e5e35d892f85f80e4db2599a5676a3b1d4f190330ed3256b26d0e80a0e49a8fffaaad2a24f472d2573241d4d6d6c7480c80b4c67bb4479c15ada7ea8424d2502fa01472e760241713dab025ae1b02e1703a1435f62ddf4ee4c1b664066eb22f2e3bf28bb70a2a76e4fd5ebe2d1229681b5b06439ac9c7e9d8bde283 - Q = 0xf85f0f83ac4df7ea0cdf8f469bfeeaea14156495 - G = 0x2b3152ff6c62f14622b8f48e59f8af46883b38e79b8c74deeae9df131f8b856e3ad6c8455dab87cc0da8ac973417ce4f7878557d6cdf40b35b4a0ca3eb310c6a95d68ce284ad4e25ea28591611ee08b8444bd64b25f3f7c572410ddfb39cc728b9c936f85f419129869929cdb909a6a3a99bbe089216368171bd0ba81de4fe33 - X = 0xc53eae6d45323164c7d07af5715703744a63fc3a - Y = 0x313fd9ebca91574e1c2eebe1517c57e0c21b0209872140c5328761bbb2450b33f1b18b409ce9ab7c4cd8fda3391e8e34868357c199e16a6b2eba06d6749def791d79e95d3a4d09b24c392ad89dbf100995ae19c01062056bb14bce005e8731efde175f95b975089bdcdaea562b32786d96f5a31aedf75364008ad4fffebb970b - - key_pub = DSA.construct((Y, G, P, Q)) - key_priv = DSA.construct((Y, G, P, Q, X)) - - def shortDescription(self): - return "FIPS DSA Tests" - - def test_loopback(self): - hashed_msg = SHA512.new(b"test") - signer = DSS.new(self.key_priv, 'fips-186-3') - signature = signer.sign(hashed_msg) - - verifier = DSS.new(self.key_pub, 'fips-186-3') - verifier.verify(hashed_msg, signature) - - def test_negative_unapproved_hashes(self): - """Verify that unapproved hashes are rejected""" - - from Cryptodome.Hash import RIPEMD160 - - self.description = "Unapproved hash (RIPEMD160) test" - hash_obj = RIPEMD160.new() - signer = DSS.new(self.key_priv, 'fips-186-3') - self.assertRaises(ValueError, signer.sign, hash_obj) - self.assertRaises(ValueError, signer.verify, hash_obj, b"\x00" * 40) - - def test_negative_unknown_modes_encodings(self): - """Verify that unknown modes/encodings are rejected""" - - self.description = "Unknown mode test" - self.assertRaises(ValueError, DSS.new, self.key_priv, 'fips-186-0') - - self.description = "Unknown encoding test" - self.assertRaises(ValueError, DSS.new, self.key_priv, 'fips-186-3', 'xml') - - def test_asn1_encoding(self): - """Verify ASN.1 encoding""" - - self.description = "ASN.1 encoding test" - hash_obj = SHA1.new() - signer = DSS.new(self.key_priv, 'fips-186-3', 'der') - signature = signer.sign(hash_obj) - - # Verify that output looks like a DER SEQUENCE - self.assertEqual(bord(signature[0]), 48) - signer.verify(hash_obj, signature) - - # Verify that ASN.1 parsing fails as expected - signature = bchr(7) + signature[1:] - self.assertRaises(ValueError, signer.verify, hash_obj, signature) - - def test_sign_verify(self): - """Verify public/private method""" - - self.description = "can_sign() test" - signer = DSS.new(self.key_priv, 'fips-186-3') - self.assertTrue(signer.can_sign()) - - signer = DSS.new(self.key_pub, 'fips-186-3') - self.assertFalse(signer.can_sign()) - - try: - signer.sign(SHA256.new(b'xyz')) - except TypeError as e: - msg = str(e) - else: - msg = "" - self.assertTrue("Private key is needed" in msg) - - -class FIPS_DSA_Tests_KAT(unittest.TestCase): - pass - - -test_vectors_verify = load_test_vectors(("Signature", "DSA"), - "FIPS_186_3_SigVer.rsp", - "Signature Verification 186-3", - {'result': lambda x: x}) or [] - -for idx, tv in enumerate(test_vectors_verify): - - if isinstance(tv, str): - res = re.match(r"\[mod = L=([0-9]+), N=([0-9]+), ([a-zA-Z0-9-]+)\]", tv) - assert(res) - hash_name = res.group(3).replace("-", "") - hash_module = load_hash_by_name(hash_name) - continue - - if hasattr(tv, "p"): - modulus = tv.p - generator = tv.g - suborder = tv.q - continue - - hash_obj = hash_module.new(tv.msg) - - comps = [bytes_to_long(x) for x in (tv.y, generator, modulus, suborder)] - key = DSA.construct(comps, False) # type: ignore - verifier = DSS.new(key, 'fips-186-3') - - def positive_test(self, verifier=verifier, hash_obj=hash_obj, signature=tv.r+tv.s): - verifier.verify(hash_obj, signature) - - def negative_test(self, verifier=verifier, hash_obj=hash_obj, signature=tv.r+tv.s): - self.assertRaises(ValueError, verifier.verify, hash_obj, signature) - - if tv.result == 'p': - setattr(FIPS_DSA_Tests_KAT, "test_verify_positive_%d" % idx, positive_test) - else: - setattr(FIPS_DSA_Tests_KAT, "test_verify_negative_%d" % idx, negative_test) - - -test_vectors_sign = load_test_vectors(("Signature", "DSA"), - "FIPS_186_3_SigGen.txt", - "Signature Creation 186-3", - {}) or [] - -for idx, tv in enumerate(test_vectors_sign): - - if isinstance(tv, str): - res = re.match(r"\[mod = L=([0-9]+), N=([0-9]+), ([a-zA-Z0-9-]+)\]", tv) - assert(res) - hash_name = res.group(3).replace("-", "") - hash_module = load_hash_by_name(hash_name) - continue - - if hasattr(tv, "p"): - modulus = tv.p - generator = tv.g - suborder = tv.q - continue - - hash_obj = hash_module.new(tv.msg) - comps_dsa = [bytes_to_long(x) for x in (tv.y, generator, modulus, suborder, tv.x)] - key = DSA.construct(comps_dsa, False) # type: ignore - signer = DSS.new(key, 'fips-186-3', randfunc=StrRNG(tv.k)) - - def new_test(self, signer=signer, hash_obj=hash_obj, signature=tv.r+tv.s): - self.assertEqual(signer.sign(hash_obj), signature) - setattr(FIPS_DSA_Tests_KAT, "test_sign_%d" % idx, new_test) - - -class FIPS_ECDSA_Tests(unittest.TestCase): - - key_priv = ECC.generate(curve="P-256") - key_pub = key_priv.public_key() - - def shortDescription(self): - return "FIPS ECDSA Tests" - - def test_loopback(self): - hashed_msg = SHA512.new(b"test") - signer = DSS.new(self.key_priv, 'fips-186-3') - signature = signer.sign(hashed_msg) - - verifier = DSS.new(self.key_pub, 'fips-186-3') - verifier.verify(hashed_msg, signature) - - def test_negative_unapproved_hashes(self): - """Verify that unapproved hashes are rejected""" - - from Cryptodome.Hash import SHA1 - - self.description = "Unapproved hash (SHA-1) test" - hash_obj = SHA1.new() - signer = DSS.new(self.key_priv, 'fips-186-3') - self.assertRaises(ValueError, signer.sign, hash_obj) - self.assertRaises(ValueError, signer.verify, hash_obj, b"\x00" * 40) - - def test_negative_eddsa_key(self): - key = ECC.generate(curve="ed25519") - self.assertRaises(ValueError, DSS.new, key, 'fips-186-3') - - def test_sign_verify(self): - """Verify public/private method""" - - self.description = "can_sign() test" - signer = DSS.new(self.key_priv, 'fips-186-3') - self.assertTrue(signer.can_sign()) - - signer = DSS.new(self.key_pub, 'fips-186-3') - self.assertFalse(signer.can_sign()) - self.assertRaises(TypeError, signer.sign, SHA256.new(b'xyz')) - - try: - signer.sign(SHA256.new(b'xyz')) - except TypeError as e: - msg = str(e) - else: - msg = "" - self.assertTrue("Private key is needed" in msg) - - def test_negative_unknown_modes_encodings(self): - """Verify that unknown modes/encodings are rejected""" - - self.description = "Unknown mode test" - self.assertRaises(ValueError, DSS.new, self.key_priv, 'fips-186-0') - - self.description = "Unknown encoding test" - self.assertRaises(ValueError, DSS.new, self.key_priv, 'fips-186-3', 'xml') - - def test_asn1_encoding(self): - """Verify ASN.1 encoding""" - - self.description = "ASN.1 encoding test" - hash_obj = SHA256.new() - signer = DSS.new(self.key_priv, 'fips-186-3', 'der') - signature = signer.sign(hash_obj) - - # Verify that output looks like a DER SEQUENCE - self.assertEqual(bord(signature[0]), 48) - signer.verify(hash_obj, signature) - - # Verify that ASN.1 parsing fails as expected - signature = bchr(7) + signature[1:] - self.assertRaises(ValueError, signer.verify, hash_obj, signature) - - -class FIPS_ECDSA_Tests_KAT(unittest.TestCase): - pass - - -test_vectors_verify = load_test_vectors(("Signature", "ECDSA"), - "SigVer.rsp", - "ECDSA Signature Verification 186-3", - {'result': lambda x: x, - 'qx': lambda x: int(x, 16), - 'qy': lambda x: int(x, 16), - }) or [] -test_vectors_verify += load_test_vectors(("Signature", "ECDSA"), - "SigVer_TruncatedSHAs.rsp", - "ECDSA Signature Verification 186-3", - {'result': lambda x: x, - 'qx': lambda x: int(x, 16), - 'qy': lambda x: int(x, 16), - }) or [] - - -for idx, tv in enumerate(test_vectors_verify): - - if isinstance(tv, str): - res = re.match(r"\[(P-[0-9]+),(SHA-[0-9]+)\]", tv) - assert res - curve_name = res.group(1) - hash_name = res.group(2).replace("-", "") - if hash_name in ("SHA512224", "SHA512256"): - truncate = hash_name[-3:] - hash_name = hash_name[:-3] - else: - truncate = None - hash_module = load_hash_by_name(hash_name) - continue - - if truncate is None: - hash_obj = hash_module.new(tv.msg) - else: - hash_obj = hash_module.new(tv.msg, truncate=truncate) - ecc_key = ECC.construct(curve=curve_name, point_x=tv.qx, point_y=tv.qy) - verifier = DSS.new(ecc_key, 'fips-186-3') - - def positive_test(self, verifier=verifier, hash_obj=hash_obj, signature=tv.r+tv.s): - verifier.verify(hash_obj, signature) - - def negative_test(self, verifier=verifier, hash_obj=hash_obj, signature=tv.r+tv.s): - self.assertRaises(ValueError, verifier.verify, hash_obj, signature) - - if tv.result.startswith('p'): - setattr(FIPS_ECDSA_Tests_KAT, "test_verify_positive_%d" % idx, positive_test) - else: - setattr(FIPS_ECDSA_Tests_KAT, "test_verify_negative_%d" % idx, negative_test) - - -test_vectors_sign = load_test_vectors(("Signature", "ECDSA"), - "SigGen.txt", - "ECDSA Signature Verification 186-3", - {'d': lambda x: int(x, 16)}) or [] - -for idx, tv in enumerate(test_vectors_sign): - - if isinstance(tv, str): - res = re.match(r"\[(P-[0-9]+),(SHA-[0-9]+)\]", tv) - assert res - curve_name = res.group(1) - hash_name = res.group(2).replace("-", "") - hash_module = load_hash_by_name(hash_name) - continue - - hash_obj = hash_module.new(tv.msg) - ecc_key = ECC.construct(curve=curve_name, d=tv.d) - signer = DSS.new(ecc_key, 'fips-186-3', randfunc=StrRNG(tv.k)) - - def sign_test(self, signer=signer, hash_obj=hash_obj, signature=tv.r+tv.s): - self.assertEqual(signer.sign(hash_obj), signature) - setattr(FIPS_ECDSA_Tests_KAT, "test_sign_%d" % idx, sign_test) - - -class Det_DSA_Tests(unittest.TestCase): - """Tests from rfc6979""" - - # Each key is (p, q, g, x, y, desc) - keys = [ - ( - """ - 86F5CA03DCFEB225063FF830A0C769B9DD9D6153AD91D7CE27F787C43278B447 - E6533B86B18BED6E8A48B784A14C252C5BE0DBF60B86D6385BD2F12FB763ED88 - 73ABFD3F5BA2E0A8C0A59082EAC056935E529DAF7C610467899C77ADEDFC846C - 881870B7B19B2B58F9BE0521A17002E3BDD6B86685EE90B3D9A1B02B782B1779""", - "996F967F6C8E388D9E28D01E205FBA957A5698B1", - """ - 07B0F92546150B62514BB771E2A0C0CE387F03BDA6C56B505209FF25FD3C133D - 89BBCD97E904E09114D9A7DEFDEADFC9078EA544D2E401AEECC40BB9FBBF78FD - 87995A10A1C27CB7789B594BA7EFB5C4326A9FE59A070E136DB77175464ADCA4 - 17BE5DCE2F40D10A46A3A3943F26AB7FD9C0398FF8C76EE0A56826A8A88F1DBD""", - "411602CB19A6CCC34494D79D98EF1E7ED5AF25F7", - """ - 5DF5E01DED31D0297E274E1691C192FE5868FEF9E19A84776454B100CF16F653 - 92195A38B90523E2542EE61871C0440CB87C322FC4B4D2EC5E1E7EC766E1BE8D - 4CE935437DC11C3C8FD426338933EBFE739CB3465F4D3668C5E473508253B1E6 - 82F65CBDC4FAE93C2EA212390E54905A86E2223170B44EAA7DA5DD9FFCFB7F3B""", - "DSA1024" - ), - ( - """ - 9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48 - C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44F - FE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5 - B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE2 - 35567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41 - F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE - 92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA15 - 3E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B""", - "F2C3119374CE76C9356990B465374A17F23F9ED35089BD969F61C6DDE9998C1F", - """ - 5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613 - D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C4 - 6A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472 - 085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5 - AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA - 3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71 - BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0 - DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7""", - "69C7548C21D0DFEA6B9A51C9EAD4E27C33D3B3F180316E5BCAB92C933F0E4DBC", - """ - 667098C654426C78D7F8201EAC6C203EF030D43605032C2F1FA937E5237DBD94 - 9F34A0A2564FE126DC8B715C5141802CE0979C8246463C40E6B6BDAA2513FA61 - 1728716C2E4FD53BC95B89E69949D96512E873B9C8F8DFD499CC312882561ADE - CB31F658E934C0C197F2C4D96B05CBAD67381E7B768891E4DA3843D24D94CDFB - 5126E9B8BF21E8358EE0E0A30EF13FD6A664C0DCE3731F7FB49A4845A4FD8254 - 687972A2D382599C9BAC4E0ED7998193078913032558134976410B89D2C171D1 - 23AC35FD977219597AA7D15C1A9A428E59194F75C721EBCBCFAE44696A499AFA - 74E04299F132026601638CB87AB79190D4A0986315DA8EEC6561C938996BEADF""", - "DSA2048" - ), - ] - - # This is a sequence of items: - # message, k, r, s, hash module - signatures = [ - ( - "sample", - "7BDB6B0FF756E1BB5D53583EF979082F9AD5BD5B", - "2E1A0C2562B2912CAAF89186FB0F42001585DA55", - "29EFB6B0AFF2D7A68EB70CA313022253B9A88DF5", - SHA1, - 'DSA1024' - ), - ( - "sample", - "562097C06782D60C3037BA7BE104774344687649", - "4BC3B686AEA70145856814A6F1BB53346F02101E", - "410697B92295D994D21EDD2F4ADA85566F6F94C1", - SHA224, - 'DSA1024' - ), - ( - "sample", - "519BA0546D0C39202A7D34D7DFA5E760B318BCFB", - "81F2F5850BE5BC123C43F71A3033E9384611C545", - "4CDD914B65EB6C66A8AAAD27299BEE6B035F5E89", - SHA256, - 'DSA1024' - ), - ( - "sample", - "95897CD7BBB944AA932DBC579C1C09EB6FCFC595", - "07F2108557EE0E3921BC1774F1CA9B410B4CE65A", - "54DF70456C86FAC10FAB47C1949AB83F2C6F7595", - SHA384, - 'DSA1024' - ), - ( - "sample", - "09ECE7CA27D0F5A4DD4E556C9DF1D21D28104F8B", - "16C3491F9B8C3FBBDD5E7A7B667057F0D8EE8E1B", - "02C36A127A7B89EDBB72E4FFBC71DABC7D4FC69C", - SHA512, - 'DSA1024' - ), - ( - "test", - "5C842DF4F9E344EE09F056838B42C7A17F4A6433", - "42AB2052FD43E123F0607F115052A67DCD9C5C77", - "183916B0230D45B9931491D4C6B0BD2FB4AAF088", - SHA1, - 'DSA1024' - ), - ( - "test", - "4598B8EFC1A53BC8AECD58D1ABBB0C0C71E67297", - "6868E9964E36C1689F6037F91F28D5F2C30610F2", - "49CEC3ACDC83018C5BD2674ECAAD35B8CD22940F", - SHA224, - 'DSA1024' - ), - ( - "test", - "5A67592E8128E03A417B0484410FB72C0B630E1A", - "22518C127299B0F6FDC9872B282B9E70D0790812", - "6837EC18F150D55DE95B5E29BE7AF5D01E4FE160", - SHA256, - 'DSA1024' - ), - ( - "test", - "220156B761F6CA5E6C9F1B9CF9C24BE25F98CD89", - "854CF929B58D73C3CBFDC421E8D5430CD6DB5E66", - "91D0E0F53E22F898D158380676A871A157CDA622", - SHA384, - 'DSA1024' - ), - ( - "test", - "65D2C2EEB175E370F28C75BFCDC028D22C7DBE9C", - "8EA47E475BA8AC6F2D821DA3BD212D11A3DEB9A0", - "7C670C7AD72B6C050C109E1790008097125433E8", - SHA512, - 'DSA1024' - ), - ( - "sample", - "888FA6F7738A41BDC9846466ABDB8174C0338250AE50CE955CA16230F9CBD53E", - "3A1B2DBD7489D6ED7E608FD036C83AF396E290DBD602408E8677DAABD6E7445A", - "D26FCBA19FA3E3058FFC02CA1596CDBB6E0D20CB37B06054F7E36DED0CDBBCCF", - SHA1, - 'DSA2048' - ), - ( - "sample", - "BC372967702082E1AA4FCE892209F71AE4AD25A6DFD869334E6F153BD0C4D806", - "DC9F4DEADA8D8FF588E98FED0AB690FFCE858DC8C79376450EB6B76C24537E2C", - "A65A9C3BC7BABE286B195D5DA68616DA8D47FA0097F36DD19F517327DC848CEC", - SHA224, - 'DSA2048' - ), - ( - "sample", - "8926A27C40484216F052F4427CFD5647338B7B3939BC6573AF4333569D597C52", - "EACE8BDBBE353C432A795D9EC556C6D021F7A03F42C36E9BC87E4AC7932CC809", - "7081E175455F9247B812B74583E9E94F9EA79BD640DC962533B0680793A38D53", - SHA256, - 'DSA2048' - ), - ( - "sample", - "C345D5AB3DA0A5BCB7EC8F8FB7A7E96069E03B206371EF7D83E39068EC564920", - "B2DA945E91858834FD9BF616EBAC151EDBC4B45D27D0DD4A7F6A22739F45C00B", - "19048B63D9FD6BCA1D9BAE3664E1BCB97F7276C306130969F63F38FA8319021B", - SHA384, - 'DSA2048' - ), - ( - "sample", - "5A12994431785485B3F5F067221517791B85A597B7A9436995C89ED0374668FC", - "2016ED092DC5FB669B8EFB3D1F31A91EECB199879BE0CF78F02BA062CB4C942E", - "D0C76F84B5F091E141572A639A4FB8C230807EEA7D55C8A154A224400AFF2351", - SHA512, - 'DSA2048' - ), - ( - "test", - "6EEA486F9D41A037B2C640BC5645694FF8FF4B98D066A25F76BE641CCB24BA4F", - "C18270A93CFC6063F57A4DFA86024F700D980E4CF4E2CB65A504397273D98EA0", - "414F22E5F31A8B6D33295C7539C1C1BA3A6160D7D68D50AC0D3A5BEAC2884FAA", - SHA1, - 'DSA2048' - ), - ( - "test", - "06BD4C05ED74719106223BE33F2D95DA6B3B541DAD7BFBD7AC508213B6DA6670", - "272ABA31572F6CC55E30BF616B7A265312018DD325BE031BE0CC82AA17870EA3", - "E9CC286A52CCE201586722D36D1E917EB96A4EBDB47932F9576AC645B3A60806", - SHA224, - 'DSA2048' - ), - ( - "test", - "1D6CE6DDA1C5D37307839CD03AB0A5CBB18E60D800937D67DFB4479AAC8DEAD7", - "8190012A1969F9957D56FCCAAD223186F423398D58EF5B3CEFD5A4146A4476F0", - "7452A53F7075D417B4B013B278D1BB8BBD21863F5E7B1CEE679CF2188E1AB19E", - SHA256, - 'DSA2048' - ), - ( - "test", - "206E61F73DBE1B2DC8BE736B22B079E9DACD974DB00EEBBC5B64CAD39CF9F91C", - "239E66DDBE8F8C230A3D071D601B6FFBDFB5901F94D444C6AF56F732BEB954BE", - "6BD737513D5E72FE85D1C750E0F73921FE299B945AAD1C802F15C26A43D34961", - SHA384, - 'DSA2048' - ), - ( - "test", - "AFF1651E4CD6036D57AA8B2A05CCF1A9D5A40166340ECBBDC55BE10B568AA0AA", - "89EC4BB1400ECCFF8E7D9AA515CD1DE7803F2DAFF09693EE7FD1353E90A68307", - "C9F0BDABCC0D880BB137A994CC7F3980CE91CC10FAF529FC46565B15CEA854E1", - SHA512, - 'DSA2048' - ) - ] - - def setUp(self): - # Convert DSA key components from hex strings to integers - # Each key is (p, q, g, x, y, desc) - - from collections import namedtuple - - TestKey = namedtuple('TestKey', 'p q g x y') - new_keys = {} - for k in self.keys: - tk = TestKey(*[t2l(y) for y in k[:-1]]) - new_keys[k[-1]] = tk - self.keys = new_keys - - # Convert signature encoding - TestSig = namedtuple('TestSig', 'message nonce result module test_key') - new_signatures = [] - for message, nonce, r, s, module, test_key in self.signatures: - tsig = TestSig( - tobytes(message), - t2l(nonce), - t2b(r) + t2b(s), - module, - self.keys[test_key] - ) - new_signatures.append(tsig) - self.signatures = new_signatures - - def test1(self): - q = 0x4000000000000000000020108A2E0CC0D99F8A5EF - x = 0x09A4D6792295A7F730FC3F2B49CBC0F62E862272F - p = 2 * q + 1 - y = pow(2, x, p) - key = DSA.construct([pow(y, 2, p), 2, p, q, x], False) - signer = DSS.new(key, 'deterministic-rfc6979') - - # Test _int2octets - self.assertEqual(hexlify(signer._int2octets(x)), - b'009a4d6792295a7f730fc3f2b49cbc0f62e862272f') - - # Test _bits2octets - h1 = SHA256.new(b"sample").digest() - self.assertEqual(hexlify(signer._bits2octets(h1)), - b'01795edf0d54db760f156d0dac04c0322b3a204224') - - def test2(self): - - for sig in self.signatures: - tk = sig.test_key - key = DSA.construct([tk.y, tk.g, tk.p, tk.q, tk.x], False) - signer = DSS.new(key, 'deterministic-rfc6979') - - hash_obj = sig.module.new(sig.message) - result = signer.sign(hash_obj) - self.assertEqual(sig.result, result) - - -class Det_ECDSA_Tests(unittest.TestCase): - - key_priv_p192 = ECC.construct(curve="P-192", d=0x6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4) - key_pub_p192 = key_priv_p192.public_key() - - key_priv_p224 = ECC.construct(curve="P-224", d=0xF220266E1105BFE3083E03EC7A3A654651F45E37167E88600BF257C1) - key_pub_p224 = key_priv_p224.public_key() - - key_priv_p256 = ECC.construct(curve="P-256", d=0xC9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721) - key_pub_p256 = key_priv_p256.public_key() - - key_priv_p384 = ECC.construct(curve="P-384", d=0x6B9D3DAD2E1B8C1C05B19875B6659F4DE23C3B667BF297BA9AA47740787137D896D5724E4C70A825F872C9EA60D2EDF5) - key_pub_p384 = key_priv_p384.public_key() - - key_priv_p521 = ECC.construct(curve="P-521", d=0x0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538) - key_pub_p521 = key_priv_p521.public_key() - - # This is a sequence of items: - # message, k, r, s, hash module - # taken from RFC6979 - signatures_p192_ = ( - ( - "sample", - "37D7CA00D2C7B0E5E412AC03BD44BA837FDD5B28CD3B0021", - "98C6BD12B23EAF5E2A2045132086BE3EB8EBD62ABF6698FF", - "57A22B07DEA9530F8DE9471B1DC6624472E8E2844BC25B64", - SHA1 - ), - ( - "sample", - "4381526B3FC1E7128F202E194505592F01D5FF4C5AF015D8", - "A1F00DAD97AEEC91C95585F36200C65F3C01812AA60378F5", - "E07EC1304C7C6C9DEBBE980B9692668F81D4DE7922A0F97A", - SHA224 - ), - ( - "sample", - "32B1B6D7D42A05CB449065727A84804FB1A3E34D8F261496", - "4B0B8CE98A92866A2820E20AA6B75B56382E0F9BFD5ECB55", - "CCDB006926EA9565CBADC840829D8C384E06DE1F1E381B85", - SHA256 - ), - ( - "sample", - "4730005C4FCB01834C063A7B6760096DBE284B8252EF4311", - "DA63BF0B9ABCF948FBB1E9167F136145F7A20426DCC287D5", - "C3AA2C960972BD7A2003A57E1C4C77F0578F8AE95E31EC5E", - SHA384 - ), - ( - "sample", - "A2AC7AB055E4F20692D49209544C203A7D1F2C0BFBC75DB1", - "4D60C5AB1996BD848343B31C00850205E2EA6922DAC2E4B8", - "3F6E837448F027A1BF4B34E796E32A811CBB4050908D8F67", - SHA512 - ), - ( - "test", - "D9CF9C3D3297D3260773A1DA7418DB5537AB8DD93DE7FA25", - "0F2141A0EBBC44D2E1AF90A50EBCFCE5E197B3B7D4DE036D", - "EB18BC9E1F3D7387500CB99CF5F7C157070A8961E38700B7", - SHA1 - ), - ( - "test", - "F5DC805F76EF851800700CCE82E7B98D8911B7D510059FBE", - "6945A1C1D1B2206B8145548F633BB61CEF04891BAF26ED34", - "B7FB7FDFC339C0B9BD61A9F5A8EAF9BE58FC5CBA2CB15293", - SHA224 - ), - ( - "test", - "5C4CE89CF56D9E7C77C8585339B006B97B5F0680B4306C6C", - "3A718BD8B4926C3B52EE6BBE67EF79B18CB6EB62B1AD97AE", - "5662E6848A4A19B1F1AE2F72ACD4B8BBE50F1EAC65D9124F", - SHA256 - ), - ( - "test", - "5AFEFB5D3393261B828DB6C91FBC68C230727B030C975693", - "B234B60B4DB75A733E19280A7A6034BD6B1EE88AF5332367", - "7994090B2D59BB782BE57E74A44C9A1C700413F8ABEFE77A", - SHA384 - ), - ( - "test", - "0758753A5254759C7CFBAD2E2D9B0792EEE44136C9480527", - "FE4F4AE86A58B6507946715934FE2D8FF9D95B6B098FE739", - "74CF5605C98FBA0E1EF34D4B5A1577A7DCF59457CAE52290", - SHA512 - ) - ) - - signatures_p224_ = ( - ( - "sample", - "7EEFADD91110D8DE6C2C470831387C50D3357F7F4D477054B8B426BC", - "22226F9D40A96E19C4A301CE5B74B115303C0F3A4FD30FC257FB57AC", - "66D1CDD83E3AF75605DD6E2FEFF196D30AA7ED7A2EDF7AF475403D69", - SHA1 - ), - ( - "sample", - "C1D1F2F10881088301880506805FEB4825FE09ACB6816C36991AA06D", - "1CDFE6662DDE1E4A1EC4CDEDF6A1F5A2FB7FBD9145C12113E6ABFD3E", - "A6694FD7718A21053F225D3F46197CA699D45006C06F871808F43EBC", - SHA224 - ), - ( - "sample", - "AD3029E0278F80643DE33917CE6908C70A8FF50A411F06E41DEDFCDC", - "61AA3DA010E8E8406C656BC477A7A7189895E7E840CDFE8FF42307BA", - "BC814050DAB5D23770879494F9E0A680DC1AF7161991BDE692B10101", - SHA256 - ), - ( - "sample", - "52B40F5A9D3D13040F494E83D3906C6079F29981035C7BD51E5CAC40", - "0B115E5E36F0F9EC81F1325A5952878D745E19D7BB3EABFABA77E953", - "830F34CCDFE826CCFDC81EB4129772E20E122348A2BBD889A1B1AF1D", - SHA384 - ), - ( - "sample", - "9DB103FFEDEDF9CFDBA05184F925400C1653B8501BAB89CEA0FBEC14", - "074BD1D979D5F32BF958DDC61E4FB4872ADCAFEB2256497CDAC30397", - "A4CECA196C3D5A1FF31027B33185DC8EE43F288B21AB342E5D8EB084", - SHA512 - ), - ( - "test", - "2519178F82C3F0E4F87ED5883A4E114E5B7A6E374043D8EFD329C253", - "DEAA646EC2AF2EA8AD53ED66B2E2DDAA49A12EFD8356561451F3E21C", - "95987796F6CF2062AB8135271DE56AE55366C045F6D9593F53787BD2", - SHA1 - ), - ( - "test", - "DF8B38D40DCA3E077D0AC520BF56B6D565134D9B5F2EAE0D34900524", - "C441CE8E261DED634E4CF84910E4C5D1D22C5CF3B732BB204DBEF019", - "902F42847A63BDC5F6046ADA114953120F99442D76510150F372A3F4", - SHA224 - ), - ( - "test", - "FF86F57924DA248D6E44E8154EB69F0AE2AEBAEE9931D0B5A969F904", - "AD04DDE87B84747A243A631EA47A1BA6D1FAA059149AD2440DE6FBA6", - "178D49B1AE90E3D8B629BE3DB5683915F4E8C99FDF6E666CF37ADCFD", - SHA256 - ), - ( - "test", - "7046742B839478C1B5BD31DB2E862AD868E1A45C863585B5F22BDC2D", - "389B92682E399B26518A95506B52C03BC9379A9DADF3391A21FB0EA4", - "414A718ED3249FF6DBC5B50C27F71F01F070944DA22AB1F78F559AAB", - SHA384 - ), - ( - "test", - "E39C2AA4EA6BE2306C72126D40ED77BF9739BB4D6EF2BBB1DCB6169D", - "049F050477C5ADD858CAC56208394B5A55BAEBBE887FDF765047C17C", - "077EB13E7005929CEFA3CD0403C7CDCC077ADF4E44F3C41B2F60ECFF", - SHA512 - ) - ) - - signatures_p256_ = ( - ( - "sample", - "882905F1227FD620FBF2ABF21244F0BA83D0DC3A9103DBBEE43A1FB858109DB4", - "61340C88C3AAEBEB4F6D667F672CA9759A6CCAA9FA8811313039EE4A35471D32", - "6D7F147DAC089441BB2E2FE8F7A3FA264B9C475098FDCF6E00D7C996E1B8B7EB", - SHA1 - ), - ( - "sample", - "103F90EE9DC52E5E7FB5132B7033C63066D194321491862059967C715985D473", - "53B2FFF5D1752B2C689DF257C04C40A587FABABB3F6FC2702F1343AF7CA9AA3F", - "B9AFB64FDC03DC1A131C7D2386D11E349F070AA432A4ACC918BEA988BF75C74C", - SHA224 - ), - ( - "sample", - "A6E3C57DD01ABE90086538398355DD4C3B17AA873382B0F24D6129493D8AAD60", - "EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716", - "F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8", - SHA256 - ), - ( - "sample", - "09F634B188CEFD98E7EC88B1AA9852D734D0BC272F7D2A47DECC6EBEB375AAD4", - "0EAFEA039B20E9B42309FB1D89E213057CBF973DC0CFC8F129EDDDC800EF7719", - "4861F0491E6998B9455193E34E7B0D284DDD7149A74B95B9261F13ABDE940954", - SHA384 - ), - ( - "sample", - "5FA81C63109BADB88C1F367B47DA606DA28CAD69AA22C4FE6AD7DF73A7173AA5", - "8496A60B5E9B47C825488827E0495B0E3FA109EC4568FD3F8D1097678EB97F00", - "2362AB1ADBE2B8ADF9CB9EDAB740EA6049C028114F2460F96554F61FAE3302FE", - SHA512 - ), - ( - "test", - "8C9520267C55D6B980DF741E56B4ADEE114D84FBFA2E62137954164028632A2E", - "0CBCC86FD6ABD1D99E703E1EC50069EE5C0B4BA4B9AC60E409E8EC5910D81A89", - "01B9D7B73DFAA60D5651EC4591A0136F87653E0FD780C3B1BC872FFDEAE479B1", - SHA1 - ), - ( - "test", - "669F4426F2688B8BE0DB3A6BD1989BDAEFFF84B649EEB84F3DD26080F667FAA7", - "C37EDB6F0AE79D47C3C27E962FA269BB4F441770357E114EE511F662EC34A692", - "C820053A05791E521FCAAD6042D40AEA1D6B1A540138558F47D0719800E18F2D", - SHA224 - ), - ( - "test", - "D16B6AE827F17175E040871A1C7EC3500192C4C92677336EC2537ACAEE0008E0", - "F1ABB023518351CD71D881567B1EA663ED3EFCF6C5132B354F28D3B0B7D38367", - "019F4113742A2B14BD25926B49C649155F267E60D3814B4C0CC84250E46F0083", - SHA256 - ), - ( - "test", - "16AEFFA357260B04B1DD199693960740066C1A8F3E8EDD79070AA914D361B3B8", - "83910E8B48BB0C74244EBDF7F07A1C5413D61472BD941EF3920E623FBCCEBEB6", - "8DDBEC54CF8CD5874883841D712142A56A8D0F218F5003CB0296B6B509619F2C", - SHA384 - ), - ( - "test", - "6915D11632ACA3C40D5D51C08DAF9C555933819548784480E93499000D9F0B7F", - "461D93F31B6540894788FD206C07CFA0CC35F46FA3C91816FFF1040AD1581A04", - "39AF9F15DE0DB8D97E72719C74820D304CE5226E32DEDAE67519E840D1194E55", - SHA512 - ) - ) - - signatures_p384_ = ( - ( - "sample", - "4471EF7518BB2C7C20F62EAE1C387AD0C5E8E470995DB4ACF694466E6AB096630F29E5938D25106C3C340045A2DB01A7", - "EC748D839243D6FBEF4FC5C4859A7DFFD7F3ABDDF72014540C16D73309834FA37B9BA002899F6FDA3A4A9386790D4EB2", - "A3BCFA947BEEF4732BF247AC17F71676CB31A847B9FF0CBC9C9ED4C1A5B3FACF26F49CA031D4857570CCB5CA4424A443", - SHA1 - ), - ( - "sample", - "A4E4D2F0E729EB786B31FC20AD5D849E304450E0AE8E3E341134A5C1AFA03CAB8083EE4E3C45B06A5899EA56C51B5879", - "42356E76B55A6D9B4631C865445DBE54E056D3B3431766D0509244793C3F9366450F76EE3DE43F5A125333A6BE060122", - "9DA0C81787064021E78DF658F2FBB0B042BF304665DB721F077A4298B095E4834C082C03D83028EFBF93A3C23940CA8D", - SHA224 - ), - ( - "sample", - "180AE9F9AEC5438A44BC159A1FCB277C7BE54FA20E7CF404B490650A8ACC414E375572342863C899F9F2EDF9747A9B60", - "21B13D1E013C7FA1392D03C5F99AF8B30C570C6F98D4EA8E354B63A21D3DAA33BDE1E888E63355D92FA2B3C36D8FB2CD", - "F3AA443FB107745BF4BD77CB3891674632068A10CA67E3D45DB2266FA7D1FEEBEFDC63ECCD1AC42EC0CB8668A4FA0AB0", - SHA256 - ), - ( - "sample", - "94ED910D1A099DAD3254E9242AE85ABDE4BA15168EAF0CA87A555FD56D10FBCA2907E3E83BA95368623B8C4686915CF9", - "94EDBB92A5ECB8AAD4736E56C691916B3F88140666CE9FA73D64C4EA95AD133C81A648152E44ACF96E36DD1E80FABE46", - "99EF4AEB15F178CEA1FE40DB2603138F130E740A19624526203B6351D0A3A94FA329C145786E679E7B82C71A38628AC8", - SHA384 - ), - ( - "sample", - "92FC3C7183A883E24216D1141F1A8976C5B0DD797DFA597E3D7B32198BD35331A4E966532593A52980D0E3AAA5E10EC3", - "ED0959D5880AB2D869AE7F6C2915C6D60F96507F9CB3E047C0046861DA4A799CFE30F35CC900056D7C99CD7882433709", - "512C8CCEEE3890A84058CE1E22DBC2198F42323CE8ACA9135329F03C068E5112DC7CC3EF3446DEFCEB01A45C2667FDD5", - SHA512 - ), - ( - "test", - "66CC2C8F4D303FC962E5FF6A27BD79F84EC812DDAE58CF5243B64A4AD8094D47EC3727F3A3C186C15054492E30698497", - "4BC35D3A50EF4E30576F58CD96CE6BF638025EE624004A1F7789A8B8E43D0678ACD9D29876DAF46638645F7F404B11C7", - "D5A6326C494ED3FF614703878961C0FDE7B2C278F9A65FD8C4B7186201A2991695BA1C84541327E966FA7B50F7382282", - SHA1 - ), - ( - "test", - "18FA39DB95AA5F561F30FA3591DC59C0FA3653A80DAFFA0B48D1A4C6DFCBFF6E3D33BE4DC5EB8886A8ECD093F2935726", - "E8C9D0B6EA72A0E7837FEA1D14A1A9557F29FAA45D3E7EE888FC5BF954B5E62464A9A817C47FF78B8C11066B24080E72", - "07041D4A7A0379AC7232FF72E6F77B6DDB8F09B16CCE0EC3286B2BD43FA8C6141C53EA5ABEF0D8231077A04540A96B66", - SHA224 - ), - ( - "test", - "0CFAC37587532347DC3389FDC98286BBA8C73807285B184C83E62E26C401C0FAA48DD070BA79921A3457ABFF2D630AD7", - "6D6DEFAC9AB64DABAFE36C6BF510352A4CC27001263638E5B16D9BB51D451559F918EEDAF2293BE5B475CC8F0188636B", - "2D46F3BECBCC523D5F1A1256BF0C9B024D879BA9E838144C8BA6BAEB4B53B47D51AB373F9845C0514EEFB14024787265", - SHA256 - ), - ( - "test", - "015EE46A5BF88773ED9123A5AB0807962D193719503C527B031B4C2D225092ADA71F4A459BC0DA98ADB95837DB8312EA", - "8203B63D3C853E8D77227FB377BCF7B7B772E97892A80F36AB775D509D7A5FEB0542A7F0812998DA8F1DD3CA3CF023DB", - "DDD0760448D42D8A43AF45AF836FCE4DE8BE06B485E9B61B827C2F13173923E06A739F040649A667BF3B828246BAA5A5", - SHA384 - ), - ( - "test", - "3780C4F67CB15518B6ACAE34C9F83568D2E12E47DEAB6C50A4E4EE5319D1E8CE0E2CC8A136036DC4B9C00E6888F66B6C", - "A0D5D090C9980FAF3C2CE57B7AE951D31977DD11C775D314AF55F76C676447D06FB6495CD21B4B6E340FC236584FB277", - "976984E59B4C77B0E8E4460DCA3D9F20E07B9BB1F63BEEFAF576F6B2E8B224634A2092CD3792E0159AD9CEE37659C736", - SHA512 - ), - ) - - signatures_p521_ = ( - ( - "sample", - "0089C071B419E1C2820962321787258469511958E80582E95D8378E0C2CCDB3CB42BEDE42F50E3FA3C71F5A76724281D31D9C89F0F91FC1BE4918DB1C03A5838D0F9", - "00343B6EC45728975EA5CBA6659BBB6062A5FF89EEA58BE3C80B619F322C87910FE092F7D45BB0F8EEE01ED3F20BABEC079D202AE677B243AB40B5431D497C55D75D", - "00E7B0E675A9B24413D448B8CC119D2BF7B2D2DF032741C096634D6D65D0DBE3D5694625FB9E8104D3B842C1B0E2D0B98BEA19341E8676AEF66AE4EBA3D5475D5D16", - SHA1 - ), - ( - "sample", - "0121415EC2CD7726330A61F7F3FA5DE14BE9436019C4DB8CB4041F3B54CF31BE0493EE3F427FB906393D895A19C9523F3A1D54BB8702BD4AA9C99DAB2597B92113F3", - "01776331CFCDF927D666E032E00CF776187BC9FDD8E69D0DABB4109FFE1B5E2A30715F4CC923A4A5E94D2503E9ACFED92857B7F31D7152E0F8C00C15FF3D87E2ED2E", - "0050CB5265417FE2320BBB5A122B8E1A32BD699089851128E360E620A30C7E17BA41A666AF126CE100E5799B153B60528D5300D08489CA9178FB610A2006C254B41F", - SHA224 - ), - ( - "sample", - "00EDF38AFCAAECAB4383358B34D67C9F2216C8382AAEA44A3DAD5FDC9C32575761793FEF24EB0FC276DFC4F6E3EC476752F043CF01415387470BCBD8678ED2C7E1A0", - "01511BB4D675114FE266FC4372B87682BAECC01D3CC62CF2303C92B3526012659D16876E25C7C1E57648F23B73564D67F61C6F14D527D54972810421E7D87589E1A7", - "004A171143A83163D6DF460AAF61522695F207A58B95C0644D87E52AA1A347916E4F7A72930B1BC06DBE22CE3F58264AFD23704CBB63B29B931F7DE6C9D949A7ECFC", - SHA256 - ), - ( - "sample", - "01546A108BC23A15D6F21872F7DED661FA8431DDBD922D0DCDB77CC878C8553FFAD064C95A920A750AC9137E527390D2D92F153E66196966EA554D9ADFCB109C4211", - "01EA842A0E17D2DE4F92C15315C63DDF72685C18195C2BB95E572B9C5136CA4B4B576AD712A52BE9730627D16054BA40CC0B8D3FF035B12AE75168397F5D50C67451", - "01F21A3CEE066E1961025FB048BD5FE2B7924D0CD797BABE0A83B66F1E35EEAF5FDE143FA85DC394A7DEE766523393784484BDF3E00114A1C857CDE1AA203DB65D61", - SHA384 - ), - ( - "sample", - "01DAE2EA071F8110DC26882D4D5EAE0621A3256FC8847FB9022E2B7D28E6F10198B1574FDD03A9053C08A1854A168AA5A57470EC97DD5CE090124EF52A2F7ECBFFD3", - "00C328FAFCBD79DD77850370C46325D987CB525569FB63C5D3BC53950E6D4C5F174E25A1EE9017B5D450606ADD152B534931D7D4E8455CC91F9B15BF05EC36E377FA", - "00617CCE7CF5064806C467F678D3B4080D6F1CC50AF26CA209417308281B68AF282623EAA63E5B5C0723D8B8C37FF0777B1A20F8CCB1DCCC43997F1EE0E44DA4A67A", - SHA512 - ), - ( - "test", - "00BB9F2BF4FE1038CCF4DABD7139A56F6FD8BB1386561BD3C6A4FC818B20DF5DDBA80795A947107A1AB9D12DAA615B1ADE4F7A9DC05E8E6311150F47F5C57CE8B222", - "013BAD9F29ABE20DE37EBEB823C252CA0F63361284015A3BF430A46AAA80B87B0693F0694BD88AFE4E661FC33B094CD3B7963BED5A727ED8BD6A3A202ABE009D0367", - "01E9BB81FF7944CA409AD138DBBEE228E1AFCC0C890FC78EC8604639CB0DBDC90F717A99EAD9D272855D00162EE9527567DD6A92CBD629805C0445282BBC916797FF", - SHA1 - ), - ( - "test", - "0040D09FCF3C8A5F62CF4FB223CBBB2B9937F6B0577C27020A99602C25A01136987E452988781484EDBBCF1C47E554E7FC901BC3085E5206D9F619CFF07E73D6F706", - "01C7ED902E123E6815546065A2C4AF977B22AA8EADDB68B2C1110E7EA44D42086BFE4A34B67DDC0E17E96536E358219B23A706C6A6E16BA77B65E1C595D43CAE17FB", - "0177336676304FCB343CE028B38E7B4FBA76C1C1B277DA18CAD2A8478B2A9A9F5BEC0F3BA04F35DB3E4263569EC6AADE8C92746E4C82F8299AE1B8F1739F8FD519A4", - SHA224 - ), - ( - "test", - "001DE74955EFAABC4C4F17F8E84D881D1310B5392D7700275F82F145C61E843841AF09035BF7A6210F5A431A6A9E81C9323354A9E69135D44EBD2FCAA7731B909258", - "000E871C4A14F993C6C7369501900C4BC1E9C7B0B4BA44E04868B30B41D8071042EB28C4C250411D0CE08CD197E4188EA4876F279F90B3D8D74A3C76E6F1E4656AA8", - "00CD52DBAA33B063C3A6CD8058A1FB0A46A4754B034FCC644766CA14DA8CA5CA9FDE00E88C1AD60CCBA759025299079D7A427EC3CC5B619BFBC828E7769BCD694E86", - SHA256 - ), - ( - "test", - "01F1FC4A349A7DA9A9E116BFDD055DC08E78252FF8E23AC276AC88B1770AE0B5DCEB1ED14A4916B769A523CE1E90BA22846AF11DF8B300C38818F713DADD85DE0C88", - "014BEE21A18B6D8B3C93FAB08D43E739707953244FDBE924FA926D76669E7AC8C89DF62ED8975C2D8397A65A49DCC09F6B0AC62272741924D479354D74FF6075578C", - "0133330865C067A0EAF72362A65E2D7BC4E461E8C8995C3B6226A21BD1AA78F0ED94FE536A0DCA35534F0CD1510C41525D163FE9D74D134881E35141ED5E8E95B979", - SHA384 - ), - ( - "test", - "016200813020EC986863BEDFC1B121F605C1215645018AEA1A7B215A564DE9EB1B38A67AA1128B80CE391C4FB71187654AAA3431027BFC7F395766CA988C964DC56D", - "013E99020ABF5CEE7525D16B69B229652AB6BDF2AFFCAEF38773B4B7D08725F10CDB93482FDCC54EDCEE91ECA4166B2A7C6265EF0CE2BD7051B7CEF945BABD47EE6D", - "01FBD0013C674AA79CB39849527916CE301C66EA7CE8B80682786AD60F98F7E78A19CA69EFF5C57400E3B3A0AD66CE0978214D13BAF4E9AC60752F7B155E2DE4DCE3", - SHA512 - ), - ) - - signatures_p192 = [] - for a, b, c, d, e in signatures_p192_: - new_tv = (tobytes(a), unhexlify(b), unhexlify(c), unhexlify(d), e) - signatures_p192.append(new_tv) - - signatures_p224 = [] - for a, b, c, d, e in signatures_p224_: - new_tv = (tobytes(a), unhexlify(b), unhexlify(c), unhexlify(d), e) - signatures_p224.append(new_tv) - - signatures_p256 = [] - for a, b, c, d, e in signatures_p256_: - new_tv = (tobytes(a), unhexlify(b), unhexlify(c), unhexlify(d), e) - signatures_p256.append(new_tv) - - signatures_p384 = [] - for a, b, c, d, e in signatures_p384_: - new_tv = (tobytes(a), unhexlify(b), unhexlify(c), unhexlify(d), e) - signatures_p384.append(new_tv) - - signatures_p521 = [] - for a, b, c, d, e in signatures_p521_: - new_tv = (tobytes(a), unhexlify(b), unhexlify(c), unhexlify(d), e) - signatures_p521.append(new_tv) - - def shortDescription(self): - return "Deterministic ECDSA Tests" - - def test_loopback_p192(self): - hashed_msg = SHA512.new(b"test") - signer = DSS.new(self.key_priv_p192, 'deterministic-rfc6979') - signature = signer.sign(hashed_msg) - - verifier = DSS.new(self.key_pub_p192, 'deterministic-rfc6979') - verifier.verify(hashed_msg, signature) - - def test_loopback_p224(self): - hashed_msg = SHA512.new(b"test") - signer = DSS.new(self.key_priv_p224, 'deterministic-rfc6979') - signature = signer.sign(hashed_msg) - - verifier = DSS.new(self.key_pub_p224, 'deterministic-rfc6979') - verifier.verify(hashed_msg, signature) - - def test_loopback_p256(self): - hashed_msg = SHA512.new(b"test") - signer = DSS.new(self.key_priv_p256, 'deterministic-rfc6979') - signature = signer.sign(hashed_msg) - - verifier = DSS.new(self.key_pub_p256, 'deterministic-rfc6979') - verifier.verify(hashed_msg, signature) - - def test_loopback_p384(self): - hashed_msg = SHA512.new(b"test") - signer = DSS.new(self.key_priv_p384, 'deterministic-rfc6979') - signature = signer.sign(hashed_msg) - - verifier = DSS.new(self.key_pub_p384, 'deterministic-rfc6979') - verifier.verify(hashed_msg, signature) - - def test_loopback_p521(self): - hashed_msg = SHA512.new(b"test") - signer = DSS.new(self.key_priv_p521, 'deterministic-rfc6979') - signature = signer.sign(hashed_msg) - - verifier = DSS.new(self.key_pub_p521, 'deterministic-rfc6979') - verifier.verify(hashed_msg, signature) - - def test_data_rfc6979_p192(self): - signer = DSS.new(self.key_priv_p192, 'deterministic-rfc6979') - for message, k, r, s, module in self.signatures_p192: - hash_obj = module.new(message) - result = signer.sign(hash_obj) - self.assertEqual(r + s, result) - - def test_data_rfc6979_p224(self): - signer = DSS.new(self.key_priv_p224, 'deterministic-rfc6979') - for message, k, r, s, module in self.signatures_p224: - hash_obj = module.new(message) - result = signer.sign(hash_obj) - self.assertEqual(r + s, result) - - def test_data_rfc6979_p256(self): - signer = DSS.new(self.key_priv_p256, 'deterministic-rfc6979') - for message, k, r, s, module in self.signatures_p256: - hash_obj = module.new(message) - result = signer.sign(hash_obj) - self.assertEqual(r + s, result) - - def test_data_rfc6979_p384(self): - signer = DSS.new(self.key_priv_p384, 'deterministic-rfc6979') - for message, k, r, s, module in self.signatures_p384: - hash_obj = module.new(message) - result = signer.sign(hash_obj) - self.assertEqual(r + s, result) - - def test_data_rfc6979_p521(self): - signer = DSS.new(self.key_priv_p521, 'deterministic-rfc6979') - for message, k, r, s, module in self.signatures_p521: - hash_obj = module.new(message) - result = signer.sign(hash_obj) - self.assertEqual(r + s, result) - - -def get_hash_module(hash_name): - if hash_name == "SHA-512": - hash_module = SHA512 - elif hash_name == "SHA-512/224": - hash_module = SHA512.new(truncate="224") - elif hash_name == "SHA-512/256": - hash_module = SHA512.new(truncate="256") - elif hash_name == "SHA-384": - hash_module = SHA384 - elif hash_name == "SHA-256": - hash_module = SHA256 - elif hash_name == "SHA-224": - hash_module = SHA224 - elif hash_name == "SHA-1": - hash_module = SHA1 - elif hash_name == "SHA3-224": - hash_module = SHA3_224 - elif hash_name == "SHA3-256": - hash_module = SHA3_256 - elif hash_name == "SHA3-384": - hash_module = SHA3_384 - elif hash_name == "SHA3-512": - hash_module = SHA3_512 - else: - raise ValueError("Unknown hash algorithm: " + hash_name) - return hash_module - - -class TestVectorsDSAWycheproof(unittest.TestCase): - - def __init__(self, wycheproof_warnings, slow_tests): - unittest.TestCase.__init__(self) - self._wycheproof_warnings = wycheproof_warnings - self._slow_tests = slow_tests - self._id = "None" - self.tv = [] - - def setUp(self): - - def filter_dsa(group): - return DSA.import_key(group['keyPem']) - - def filter_sha(group): - return get_hash_module(group['sha']) - - def filter_type(group): - sig_type = group['type'] - if sig_type != 'DsaVerify': - raise ValueError("Unknown signature type " + sig_type) - return sig_type - - result = load_test_vectors_wycheproof(("Signature", "wycheproof"), - "dsa_test.json", - "Wycheproof DSA signature", - group_tag={'key': filter_dsa, - 'hash_module': filter_sha, - 'sig_type': filter_type}) - self.tv += result - - def shortDescription(self): - return self._id - - def warn(self, tv): - if tv.warning and self._wycheproof_warnings: - import warnings - warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment)) - - def test_verify(self, tv): - self._id = "Wycheproof DSA Test #" + str(tv.id) - - hashed_msg = tv.hash_module.new(tv.msg) - signer = DSS.new(tv.key, 'fips-186-3', encoding='der') - try: - signature = signer.verify(hashed_msg, tv.sig) - except ValueError as e: - if tv.warning: - return - assert not tv.valid - else: - assert tv.valid - self.warn(tv) - - def runTest(self): - for tv in self.tv: - self.test_verify(tv) - - -class TestVectorsECDSAWycheproof(unittest.TestCase): - - def __init__(self, wycheproof_warnings, slow_tests): - unittest.TestCase.__init__(self) - self._wycheproof_warnings = wycheproof_warnings - self._slow_tests = slow_tests - self._id = "None" - - def add_tests(self, filename): - - def filter_ecc(group): - # These are the only curves we accept to skip - if group['key']['curve'] in ('secp224k1', 'secp256k1', - 'brainpoolP224r1', 'brainpoolP224t1', - 'brainpoolP256r1', 'brainpoolP256t1', - 'brainpoolP320r1', 'brainpoolP320t1', - 'brainpoolP384r1', 'brainpoolP384t1', - 'brainpoolP512r1', 'brainpoolP512t1', - ): - return None - return ECC.import_key(group['keyPem']) - - def filter_sha(group): - return get_hash_module(group['sha']) - - def filter_encoding(group): - encoding_name = group['type'] - if encoding_name == "EcdsaVerify": - return "der" - elif encoding_name == "EcdsaP1363Verify": - return "binary" - else: - raise ValueError("Unknown signature type " + encoding_name) - - result = load_test_vectors_wycheproof(("Signature", "wycheproof"), - filename, - "Wycheproof ECDSA signature (%s)" % filename, - group_tag={'key': filter_ecc, - 'hash_module': filter_sha, - 'encoding': filter_encoding, - }) - self.tv += result - - def setUp(self): - self.tv = [] - self.add_tests("ecdsa_secp224r1_sha224_p1363_test.json") - self.add_tests("ecdsa_secp224r1_sha224_test.json") - if self._slow_tests: - self.add_tests("ecdsa_secp224r1_sha256_p1363_test.json") - self.add_tests("ecdsa_secp224r1_sha256_test.json") - self.add_tests("ecdsa_secp224r1_sha3_224_test.json") - self.add_tests("ecdsa_secp224r1_sha3_256_test.json") - self.add_tests("ecdsa_secp224r1_sha3_512_test.json") - self.add_tests("ecdsa_secp224r1_sha512_p1363_test.json") - self.add_tests("ecdsa_secp224r1_sha512_test.json") - self.add_tests("ecdsa_secp256r1_sha256_p1363_test.json") - self.add_tests("ecdsa_secp256r1_sha256_test.json") - self.add_tests("ecdsa_secp256r1_sha3_256_test.json") - self.add_tests("ecdsa_secp256r1_sha3_512_test.json") - self.add_tests("ecdsa_secp256r1_sha512_p1363_test.json") - self.add_tests("ecdsa_secp256r1_sha512_test.json") - if self._slow_tests: - self.add_tests("ecdsa_secp384r1_sha3_384_test.json") - self.add_tests("ecdsa_secp384r1_sha3_512_test.json") - self.add_tests("ecdsa_secp384r1_sha384_p1363_test.json") - self.add_tests("ecdsa_secp384r1_sha384_test.json") - self.add_tests("ecdsa_secp384r1_sha512_p1363_test.json") - self.add_tests("ecdsa_secp384r1_sha512_test.json") - if self._slow_tests: - self.add_tests("ecdsa_secp521r1_sha3_512_test.json") - self.add_tests("ecdsa_secp521r1_sha512_p1363_test.json") - self.add_tests("ecdsa_secp521r1_sha512_test.json") - self.add_tests("ecdsa_test.json") - self.add_tests("ecdsa_webcrypto_test.json") - - def shortDescription(self): - return self._id - - def warn(self, tv): - if tv.warning and self._wycheproof_warnings: - import warnings - warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment)) - - def test_verify(self, tv): - self._id = "Wycheproof ECDSA Test #%d (%s, %s)" % (tv.id, tv.comment, tv.filename) - - # Skip tests with unsupported curves - if tv.key is None: - return - - hashed_msg = tv.hash_module.new(tv.msg) - signer = DSS.new(tv.key, 'fips-186-3', encoding=tv.encoding) - try: - signature = signer.verify(hashed_msg, tv.sig) - except ValueError as e: - if tv.warning: - return - if tv.comment == "k*G has a large x-coordinate": - return - assert not tv.valid - else: - assert tv.valid - self.warn(tv) - - def runTest(self): - for tv in self.tv: - self.test_verify(tv) - - -def get_tests(config={}): - wycheproof_warnings = config.get('wycheproof_warnings') - - tests = [] - tests += list_test_cases(FIPS_DSA_Tests) - tests += list_test_cases(FIPS_ECDSA_Tests) - tests += list_test_cases(Det_DSA_Tests) - tests += list_test_cases(Det_ECDSA_Tests) - - slow_tests = config.get('slow_tests') - if slow_tests: - tests += list_test_cases(FIPS_DSA_Tests_KAT) - tests += list_test_cases(FIPS_ECDSA_Tests_KAT) - - tests += [TestVectorsDSAWycheproof(wycheproof_warnings, slow_tests)] - tests += [TestVectorsECDSAWycheproof(wycheproof_warnings, slow_tests)] - - return tests - - -if __name__ == '__main__': - def suite(): - return unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Signature/test_eddsa.py b/resources/lib/deps/Cryptodome/SelfTest/Signature/test_eddsa.py deleted file mode 100644 index 015e247..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Signature/test_eddsa.py +++ /dev/null @@ -1,578 +0,0 @@ -# -# Copyright (c) 2022, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import unittest -from binascii import unhexlify - -from Cryptodome.PublicKey import ECC -from Cryptodome.Signature import eddsa -from Cryptodome.Hash import SHA512, SHAKE256 -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.SelfTest.loader import load_test_vectors_wycheproof -from Cryptodome.Util.number import bytes_to_long - -rfc8032_tv_str = ( - # 7.1 Ed25519 - ( - "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60", - "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a", - "", - None, - "", - "e5564300c360ac729086e2cc806e828a" - "84877f1eb8e5d974d873e06522490155" - "5fb8821590a33bacc61e39701cf9b46b" - "d25bf5f0595bbe24655141438e7a100b" - ), - ( - "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb", - "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c", - "72", - None, - "", - "92a009a9f0d4cab8720e820b5f642540" - "a2b27b5416503f8fb3762223ebdb69da" - "085ac1e43e15996e458f3613d0f11d8c" - "387b2eaeb4302aeeb00d291612bb0c00" - ), - ( - "c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7", - "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025", - "af82", - None, - "", - "6291d657deec24024827e69c3abe01a3" - "0ce548a284743a445e3680d7db5ac3ac" - "18ff9b538d16f290ae67f760984dc659" - "4a7c15e9716ed28dc027beceea1ec40a" - ), - ( - "f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5", - "278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e", - "08b8b2b733424243760fe426a4b54908" - "632110a66c2f6591eabd3345e3e4eb98" - "fa6e264bf09efe12ee50f8f54e9f77b1" - "e355f6c50544e23fb1433ddf73be84d8" - "79de7c0046dc4996d9e773f4bc9efe57" - "38829adb26c81b37c93a1b270b20329d" - "658675fc6ea534e0810a4432826bf58c" - "941efb65d57a338bbd2e26640f89ffbc" - "1a858efcb8550ee3a5e1998bd177e93a" - "7363c344fe6b199ee5d02e82d522c4fe" - "ba15452f80288a821a579116ec6dad2b" - "3b310da903401aa62100ab5d1a36553e" - "06203b33890cc9b832f79ef80560ccb9" - "a39ce767967ed628c6ad573cb116dbef" - "efd75499da96bd68a8a97b928a8bbc10" - "3b6621fcde2beca1231d206be6cd9ec7" - "aff6f6c94fcd7204ed3455c68c83f4a4" - "1da4af2b74ef5c53f1d8ac70bdcb7ed1" - "85ce81bd84359d44254d95629e9855a9" - "4a7c1958d1f8ada5d0532ed8a5aa3fb2" - "d17ba70eb6248e594e1a2297acbbb39d" - "502f1a8c6eb6f1ce22b3de1a1f40cc24" - "554119a831a9aad6079cad88425de6bd" - "e1a9187ebb6092cf67bf2b13fd65f270" - "88d78b7e883c8759d2c4f5c65adb7553" - "878ad575f9fad878e80a0c9ba63bcbcc" - "2732e69485bbc9c90bfbd62481d9089b" - "eccf80cfe2df16a2cf65bd92dd597b07" - "07e0917af48bbb75fed413d238f5555a" - "7a569d80c3414a8d0859dc65a46128ba" - "b27af87a71314f318c782b23ebfe808b" - "82b0ce26401d2e22f04d83d1255dc51a" - "ddd3b75a2b1ae0784504df543af8969b" - "e3ea7082ff7fc9888c144da2af58429e" - "c96031dbcad3dad9af0dcbaaaf268cb8" - "fcffead94f3c7ca495e056a9b47acdb7" - "51fb73e666c6c655ade8297297d07ad1" - "ba5e43f1bca32301651339e22904cc8c" - "42f58c30c04aafdb038dda0847dd988d" - "cda6f3bfd15c4b4c4525004aa06eeff8" - "ca61783aacec57fb3d1f92b0fe2fd1a8" - "5f6724517b65e614ad6808d6f6ee34df" - "f7310fdc82aebfd904b01e1dc54b2927" - "094b2db68d6f903b68401adebf5a7e08" - "d78ff4ef5d63653a65040cf9bfd4aca7" - "984a74d37145986780fc0b16ac451649" - "de6188a7dbdf191f64b5fc5e2ab47b57" - "f7f7276cd419c17a3ca8e1b939ae49e4" - "88acba6b965610b5480109c8b17b80e1" - "b7b750dfc7598d5d5011fd2dcc5600a3" - "2ef5b52a1ecc820e308aa342721aac09" - "43bf6686b64b2579376504ccc493d97e" - "6aed3fb0f9cd71a43dd497f01f17c0e2" - "cb3797aa2a2f256656168e6c496afc5f" - "b93246f6b1116398a346f1a641f3b041" - "e989f7914f90cc2c7fff357876e506b5" - "0d334ba77c225bc307ba537152f3f161" - "0e4eafe595f6d9d90d11faa933a15ef1" - "369546868a7f3a45a96768d40fd9d034" - "12c091c6315cf4fde7cb68606937380d" - "b2eaaa707b4c4185c32eddcdd306705e" - "4dc1ffc872eeee475a64dfac86aba41c" - "0618983f8741c5ef68d3a101e8a3b8ca" - "c60c905c15fc910840b94c00a0b9d0", - None, - "", - "0aab4c900501b3e24d7cdf4663326a3a" - "87df5e4843b2cbdb67cbf6e460fec350" - "aa5371b1508f9f4528ecea23c436d94b" - "5e8fcd4f681e30a6ac00a9704a188a03" - ), - # 7.2 Ed25519ctx - ( - "0305334e381af78f141cb666f6199f57" - "bc3495335a256a95bd2a55bf546663f6", - "dfc9425e4f968f7f0c29f0259cf5f9ae" - "d6851c2bb4ad8bfb860cfee0ab248292", - "f726936d19c800494e3fdaff20b276a8", - None, - "666f6f", - "55a4cc2f70a54e04288c5f4cd1e45a7b" - "b520b36292911876cada7323198dd87a" - "8b36950b95130022907a7fb7c4e9b2d5" - "f6cca685a587b4b21f4b888e4e7edb0d" - ), - ( - "0305334e381af78f141cb666f6199f57" - "bc3495335a256a95bd2a55bf546663f6", - "dfc9425e4f968f7f0c29f0259cf5f9ae" - "d6851c2bb4ad8bfb860cfee0ab248292", - "f726936d19c800494e3fdaff20b276a8", - None, - "626172", - "fc60d5872fc46b3aa69f8b5b4351d580" - "8f92bcc044606db097abab6dbcb1aee3" - "216c48e8b3b66431b5b186d1d28f8ee1" - "5a5ca2df6668346291c2043d4eb3e90d" - ), - ( - "0305334e381af78f141cb666f6199f57" - "bc3495335a256a95bd2a55bf546663f6", - "dfc9425e4f968f7f0c29f0259cf5f9ae" - "d6851c2bb4ad8bfb860cfee0ab248292", - "508e9e6882b979fea900f62adceaca35", - None, - "666f6f", - "8b70c1cc8310e1de20ac53ce28ae6e72" - "07f33c3295e03bb5c0732a1d20dc6490" - "8922a8b052cf99b7c4fe107a5abb5b2c" - "4085ae75890d02df26269d8945f84b0b" - ), - ( - "ab9c2853ce297ddab85c993b3ae14bca" - "d39b2c682beabc27d6d4eb20711d6560", - "0f1d1274943b91415889152e893d80e9" - "3275a1fc0b65fd71b4b0dda10ad7d772", - "f726936d19c800494e3fdaff20b276a8", - None, - "666f6f", - "21655b5f1aa965996b3f97b3c849eafb" - "a922a0a62992f73b3d1b73106a84ad85" - "e9b86a7b6005ea868337ff2d20a7f5fb" - "d4cd10b0be49a68da2b2e0dc0ad8960f" - ), - # 7.3 Ed25519ph - ( - "833fe62409237b9d62ec77587520911e" - "9a759cec1d19755b7da901b96dca3d42", - "ec172b93ad5e563bf4932c70e1245034" - "c35467ef2efd4d64ebf819683467e2bf", - "616263", - SHA512, - "", - "98a70222f0b8121aa9d30f813d683f80" - "9e462b469c7ff87639499bb94e6dae41" - "31f85042463c2a355a2003d062adf5aa" - "a10b8c61e636062aaad11c2a26083406" - ), - # 7.4 Ed448 - ( - "6c82a562cb808d10d632be89c8513ebf6c929f34ddfa8c9f63c9960ef6e348a3" - "528c8a3fcc2f044e39a3fc5b94492f8f032e7549a20098f95b", - "5fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80e96778" - "edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180", - "", - None, - "", - "533a37f6bbe457251f023c0d88f976ae2dfb504a843e34d2074fd823d41a591f" - "2b233f034f628281f2fd7a22ddd47d7828c59bd0a21bfd3980ff0d2028d4b18a" - "9df63e006c5d1c2d345b925d8dc00b4104852db99ac5c7cdda8530a113a0f4db" - "b61149f05a7363268c71d95808ff2e652600" - ), - ( - "c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463a" - "fbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e", - "43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c086" - "6aea01eb00742802b8438ea4cb82169c235160627b4c3a9480", - "03", - None, - "", - "26b8f91727bd62897af15e41eb43c377efb9c610d48f2335cb0bd0087810f435" - "2541b143c4b981b7e18f62de8ccdf633fc1bf037ab7cd779805e0dbcc0aae1cb" - "cee1afb2e027df36bc04dcecbf154336c19f0af7e0a6472905e799f1953d2a0f" - "f3348ab21aa4adafd1d234441cf807c03a00", - ), - ( - "c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463a" - "fbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e", - "43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c086" - "6aea01eb00742802b8438ea4cb82169c235160627b4c3a9480", - "03", - None, - "666f6f", - "d4f8f6131770dd46f40867d6fd5d5055de43541f8c5e35abbcd001b32a89f7d2" - "151f7647f11d8ca2ae279fb842d607217fce6e042f6815ea000c85741de5c8da" - "1144a6a1aba7f96de42505d7a7298524fda538fccbbb754f578c1cad10d54d0d" - "5428407e85dcbc98a49155c13764e66c3c00", - ), - ( - "cd23d24f714274e744343237b93290f511f6425f98e64459ff203e8985083ffd" - "f60500553abc0e05cd02184bdb89c4ccd67e187951267eb328", - "dcea9e78f35a1bf3499a831b10b86c90aac01cd84b67a0109b55a36e9328b1e3" - "65fce161d71ce7131a543ea4cb5f7e9f1d8b00696447001400", - "0c3e544074ec63b0265e0c", - None, - "", - "1f0a8888ce25e8d458a21130879b840a9089d999aaba039eaf3e3afa090a09d3" - "89dba82c4ff2ae8ac5cdfb7c55e94d5d961a29fe0109941e00b8dbdeea6d3b05" - "1068df7254c0cdc129cbe62db2dc957dbb47b51fd3f213fb8698f064774250a5" - "028961c9bf8ffd973fe5d5c206492b140e00", - ), - ( - "258cdd4ada32ed9c9ff54e63756ae582fb8fab2ac721f2c8e676a72768513d93" - "9f63dddb55609133f29adf86ec9929dccb52c1c5fd2ff7e21b", - "3ba16da0c6f2cc1f30187740756f5e798d6bc5fc015d7c63cc9510ee3fd44adc" - "24d8e968b6e46e6f94d19b945361726bd75e149ef09817f580", - "64a65f3cdedcdd66811e2915", - None, - "", - "7eeeab7c4e50fb799b418ee5e3197ff6bf15d43a14c34389b59dd1a7b1b85b4a" - "e90438aca634bea45e3a2695f1270f07fdcdf7c62b8efeaf00b45c2c96ba457e" - "b1a8bf075a3db28e5c24f6b923ed4ad747c3c9e03c7079efb87cb110d3a99861" - "e72003cbae6d6b8b827e4e6c143064ff3c00", - ), - ( - "7ef4e84544236752fbb56b8f31a23a10e42814f5f55ca037cdcc11c64c9a3b29" - "49c1bb60700314611732a6c2fea98eebc0266a11a93970100e", - "b3da079b0aa493a5772029f0467baebee5a8112d9d3a22532361da294f7bb381" - "5c5dc59e176b4d9f381ca0938e13c6c07b174be65dfa578e80", - "64a65f3cdedcdd66811e2915e7", - None, - "", - "6a12066f55331b6c22acd5d5bfc5d71228fbda80ae8dec26bdd306743c5027cb" - "4890810c162c027468675ecf645a83176c0d7323a2ccde2d80efe5a1268e8aca" - "1d6fbc194d3f77c44986eb4ab4177919ad8bec33eb47bbb5fc6e28196fd1caf5" - "6b4e7e0ba5519234d047155ac727a1053100", - ), - ( - "d65df341ad13e008567688baedda8e9dcdc17dc024974ea5b4227b6530e339bf" - "f21f99e68ca6968f3cca6dfe0fb9f4fab4fa135d5542ea3f01", - "df9705f58edbab802c7f8363cfe5560ab1c6132c20a9f1dd163483a26f8ac53a" - "39d6808bf4a1dfbd261b099bb03b3fb50906cb28bd8a081f00", - "bd0f6a3747cd561bdddf4640a332461a4a30a12a434cd0bf40d766d9c6d458e5" - "512204a30c17d1f50b5079631f64eb3112182da3005835461113718d1a5ef944", - None, - "", - "554bc2480860b49eab8532d2a533b7d578ef473eeb58c98bb2d0e1ce488a98b1" - "8dfde9b9b90775e67f47d4a1c3482058efc9f40d2ca033a0801b63d45b3b722e" - "f552bad3b4ccb667da350192b61c508cf7b6b5adadc2c8d9a446ef003fb05cba" - "5f30e88e36ec2703b349ca229c2670833900", - ), - ( - "2ec5fe3c17045abdb136a5e6a913e32ab75ae68b53d2fc149b77e504132d3756" - "9b7e766ba74a19bd6162343a21c8590aa9cebca9014c636df5", - "79756f014dcfe2079f5dd9e718be4171e2ef2486a08f25186f6bff43a9936b9b" - "fe12402b08ae65798a3d81e22e9ec80e7690862ef3d4ed3a00", - "15777532b0bdd0d1389f636c5f6b9ba734c90af572877e2d272dd078aa1e567c" - "fa80e12928bb542330e8409f3174504107ecd5efac61ae7504dabe2a602ede89" - "e5cca6257a7c77e27a702b3ae39fc769fc54f2395ae6a1178cab4738e543072f" - "c1c177fe71e92e25bf03e4ecb72f47b64d0465aaea4c7fad372536c8ba516a60" - "39c3c2a39f0e4d832be432dfa9a706a6e5c7e19f397964ca4258002f7c0541b5" - "90316dbc5622b6b2a6fe7a4abffd96105eca76ea7b98816af0748c10df048ce0" - "12d901015a51f189f3888145c03650aa23ce894c3bd889e030d565071c59f409" - "a9981b51878fd6fc110624dcbcde0bf7a69ccce38fabdf86f3bef6044819de11", - None, - "", - "c650ddbb0601c19ca11439e1640dd931f43c518ea5bea70d3dcde5f4191fe53f" - "00cf966546b72bcc7d58be2b9badef28743954e3a44a23f880e8d4f1cfce2d7a" - "61452d26da05896f0a50da66a239a8a188b6d825b3305ad77b73fbac0836ecc6" - "0987fd08527c1a8e80d5823e65cafe2a3d00", - ), - ( - "872d093780f5d3730df7c212664b37b8a0f24f56810daa8382cd4fa3f77634ec" - "44dc54f1c2ed9bea86fafb7632d8be199ea165f5ad55dd9ce8", - "a81b2e8a70a5ac94ffdbcc9badfc3feb0801f258578bb114ad44ece1ec0e799d" - "a08effb81c5d685c0c56f64eecaef8cdf11cc38737838cf400", - "6ddf802e1aae4986935f7f981ba3f0351d6273c0a0c22c9c0e8339168e675412" - "a3debfaf435ed651558007db4384b650fcc07e3b586a27a4f7a00ac8a6fec2cd" - "86ae4bf1570c41e6a40c931db27b2faa15a8cedd52cff7362c4e6e23daec0fbc" - "3a79b6806e316efcc7b68119bf46bc76a26067a53f296dafdbdc11c77f7777e9" - "72660cf4b6a9b369a6665f02e0cc9b6edfad136b4fabe723d2813db3136cfde9" - "b6d044322fee2947952e031b73ab5c603349b307bdc27bc6cb8b8bbd7bd32321" - "9b8033a581b59eadebb09b3c4f3d2277d4f0343624acc817804728b25ab79717" - "2b4c5c21a22f9c7839d64300232eb66e53f31c723fa37fe387c7d3e50bdf9813" - "a30e5bb12cf4cd930c40cfb4e1fc622592a49588794494d56d24ea4b40c89fc0" - "596cc9ebb961c8cb10adde976a5d602b1c3f85b9b9a001ed3c6a4d3b1437f520" - "96cd1956d042a597d561a596ecd3d1735a8d570ea0ec27225a2c4aaff26306d1" - "526c1af3ca6d9cf5a2c98f47e1c46db9a33234cfd4d81f2c98538a09ebe76998" - "d0d8fd25997c7d255c6d66ece6fa56f11144950f027795e653008f4bd7ca2dee" - "85d8e90f3dc315130ce2a00375a318c7c3d97be2c8ce5b6db41a6254ff264fa6" - "155baee3b0773c0f497c573f19bb4f4240281f0b1f4f7be857a4e59d416c06b4" - "c50fa09e1810ddc6b1467baeac5a3668d11b6ecaa901440016f389f80acc4db9" - "77025e7f5924388c7e340a732e554440e76570f8dd71b7d640b3450d1fd5f041" - "0a18f9a3494f707c717b79b4bf75c98400b096b21653b5d217cf3565c9597456" - "f70703497a078763829bc01bb1cbc8fa04eadc9a6e3f6699587a9e75c94e5bab" - "0036e0b2e711392cff0047d0d6b05bd2a588bc109718954259f1d86678a579a3" - "120f19cfb2963f177aeb70f2d4844826262e51b80271272068ef5b3856fa8535" - "aa2a88b2d41f2a0e2fda7624c2850272ac4a2f561f8f2f7a318bfd5caf969614" - "9e4ac824ad3460538fdc25421beec2cc6818162d06bbed0c40a387192349db67" - "a118bada6cd5ab0140ee273204f628aad1c135f770279a651e24d8c14d75a605" - "9d76b96a6fd857def5e0b354b27ab937a5815d16b5fae407ff18222c6d1ed263" - "be68c95f32d908bd895cd76207ae726487567f9a67dad79abec316f683b17f2d" - "02bf07e0ac8b5bc6162cf94697b3c27cd1fea49b27f23ba2901871962506520c" - "392da8b6ad0d99f7013fbc06c2c17a569500c8a7696481c1cd33e9b14e40b82e" - "79a5f5db82571ba97bae3ad3e0479515bb0e2b0f3bfcd1fd33034efc6245eddd" - "7ee2086ddae2600d8ca73e214e8c2b0bdb2b047c6a464a562ed77b73d2d841c4" - "b34973551257713b753632efba348169abc90a68f42611a40126d7cb21b58695" - "568186f7e569d2ff0f9e745d0487dd2eb997cafc5abf9dd102e62ff66cba87", - None, - "", - "e301345a41a39a4d72fff8df69c98075a0cc082b802fc9b2b6bc503f926b65bd" - "df7f4c8f1cb49f6396afc8a70abe6d8aef0db478d4c6b2970076c6a0484fe76d" - "76b3a97625d79f1ce240e7c576750d295528286f719b413de9ada3e8eb78ed57" - "3603ce30d8bb761785dc30dbc320869e1a00" - ), - # 7.5 Ed448ph - ( - "833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42" - "ef7822e0d5104127dc05d6dbefde69e3ab2cec7c867c6e2c49", - "259b71c19f83ef77a7abd26524cbdb3161b590a48f7d17de3ee0ba9c52beb743" - "c09428a131d6b1b57303d90d8132c276d5ed3d5d01c0f53880", - "616263", - SHAKE256, - "", - "822f6901f7480f3d5f562c592994d9693602875614483256505600bbc281ae38" - "1f54d6bce2ea911574932f52a4e6cadd78769375ec3ffd1b801a0d9b3f4030cd" - "433964b6457ea39476511214f97469b57dd32dbc560a9a94d00bff07620464a3" - "ad203df7dc7ce360c3cd3696d9d9fab90f00" - ), - ( - "833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42" - "ef7822e0d5104127dc05d6dbefde69e3ab2cec7c867c6e2c49", - "259b71c19f83ef77a7abd26524cbdb3161b590a48f7d17de3ee0ba9c52beb743" - "c09428a131d6b1b57303d90d8132c276d5ed3d5d01c0f53880", - "616263", - SHAKE256, - "666f6f", - "c32299d46ec8ff02b54540982814dce9a05812f81962b649d528095916a2aa48" - "1065b1580423ef927ecf0af5888f90da0f6a9a85ad5dc3f280d91224ba9911a3" - "653d00e484e2ce232521481c8658df304bb7745a73514cdb9bf3e15784ab7128" - "4f8d0704a608c54a6b62d97beb511d132100", - ), -) - - -rfc8032_tv_bytes = [] -for tv_str in rfc8032_tv_str: - rfc8032_tv_bytes.append([unhexlify(i) if isinstance(i, str) else i for i in tv_str]) - - -class TestEdDSA(unittest.TestCase): - - def test_sign(self): - for sk, _, msg, hashmod, ctx, exp_signature in rfc8032_tv_bytes: - key = eddsa.import_private_key(sk) - signer = eddsa.new(key, 'rfc8032', context=ctx) - if hashmod is None: - # PureEdDSA - signature = signer.sign(msg) - else: - # HashEdDSA - hashobj = hashmod.new(msg) - signature = signer.sign(hashobj) - self.assertEqual(exp_signature, signature) - - def test_verify(self): - for _, pk, msg, hashmod, ctx, exp_signature in rfc8032_tv_bytes: - key = eddsa.import_public_key(pk) - verifier = eddsa.new(key, 'rfc8032', context=ctx) - if hashmod is None: - # PureEdDSA - verifier.verify(msg, exp_signature) - else: - # HashEdDSA - hashobj = hashmod.new(msg) - verifier.verify(hashobj, exp_signature) - - def test_negative(self): - key = ECC.generate(curve="ed25519") - self.assertRaises(ValueError, eddsa.new, key, 'rfc9999') - - nist_key = ECC.generate(curve="p256") - self.assertRaises(ValueError, eddsa.new, nist_key, 'rfc8032') - - -class TestExport_Ed25519(unittest.TestCase): - - def test_raw(self): - key = ECC.generate(curve="Ed25519") - x, y = key.pointQ.xy - raw = bytearray(key._export_eddsa()) - sign_x = raw[31] >> 7 - raw[31] &= 0x7F - yt = bytes_to_long(raw[::-1]) - self.assertEqual(y, yt) - self.assertEqual(x & 1, sign_x) - - key = ECC.construct(point_x=0, point_y=1, curve="Ed25519") - out = key._export_eddsa() - self.assertEqual(b'\x01' + b'\x00' * 31, out) - - -class TestExport_Ed448(unittest.TestCase): - - def test_raw(self): - key = ECC.generate(curve="Ed448") - x, y = key.pointQ.xy - raw = bytearray(key._export_eddsa()) - sign_x = raw[56] >> 7 - raw[56] &= 0x7F - yt = bytes_to_long(raw[::-1]) - self.assertEqual(y, yt) - self.assertEqual(x & 1, sign_x) - - key = ECC.construct(point_x=0, point_y=1, curve="Ed448") - out = key._export_eddsa() - self.assertEqual(b'\x01' + b'\x00' * 56, out) - - -class TestImport_Ed25519(unittest.TestCase): - - def test_raw(self): - Px = 24407857220263921307776619664228778204996144802740950419837658238229122415920 - Py = 56480760040633817885061096979765646085062883740629155052073094891081309750690 - encoded = b'\xa2\x05\xd6\x00\xe1 \xe1\xc0\xff\x96\xee?V\x8e\xba/\xd3\x89\x06\xd7\xc4c\xe8$\xc2d\xd7a1\xfa\xde|' - key = eddsa.import_public_key(encoded) - self.assertEqual(Py, key.pointQ.y) - self.assertEqual(Px, key.pointQ.x) - - encoded = b'\x01' + b'\x00' * 31 - key = eddsa.import_public_key(encoded) - self.assertEqual(1, key.pointQ.y) - self.assertEqual(0, key.pointQ.x) - - -class TestImport_Ed448(unittest.TestCase): - - def test_raw(self): - Px = 0x153f42025aba3b0daecaa5cd79458b3146c7c9378c16c17b4a59bc3561113d90c169045bc12966c3f93e140c2ca0a3acc33d9205b9daf9b1 - Py = 0x38f5c0015d3dedd576c232810dd90373b5b1d631a12894c043b7be529cbae03ede177d8fa490b56131dbcb2465d2aba777ef839fc1719b25 - encoded = unhexlify("259b71c19f83ef77a7abd26524cbdb31" - "61b590a48f7d17de3ee0ba9c52beb743" - "c09428a131d6b1b57303d90d8132c276" - "d5ed3d5d01c0f53880") - key = eddsa.import_public_key(encoded) - self.assertEqual(Py, key.pointQ.y) - self.assertEqual(Px, key.pointQ.x) - - encoded = b'\x01' + b'\x00' * 56 - key = eddsa.import_public_key(encoded) - self.assertEqual(1, key.pointQ.y) - self.assertEqual(0, key.pointQ.x) - - -class TestVectorsEdDSAWycheproof(unittest.TestCase): - - def add_tests(self, filename): - - def pk(group): - elem = group['key']['pk'] - return unhexlify(elem) - - def sk(group): - elem = group['key']['sk'] - return unhexlify(elem) - - result = load_test_vectors_wycheproof(("Signature", "wycheproof"), - filename, - "Wycheproof ECDSA signature (%s)" - % filename, - group_tag={'pk': pk, 'sk': sk}) - self.tv += result - - def setUp(self): - self.tv = [] - self.add_tests("eddsa_test.json") - self.add_tests("ed448_test.json") - - def test_sign(self, tv): - if not tv.valid: - return - - self._id = "Wycheproof EdDSA Sign Test #%d (%s, %s)" % (tv.id, tv.comment, tv.filename) - key = eddsa.import_private_key(tv.sk) - signer = eddsa.new(key, 'rfc8032') - signature = signer.sign(tv.msg) - self.assertEqual(signature, tv.sig) - - def test_verify(self, tv): - self._id = "Wycheproof EdDSA Verify Test #%d (%s, %s)" % (tv.id, tv.comment, tv.filename) - key = eddsa.import_public_key(tv.pk) - verifier = eddsa.new(key, 'rfc8032') - try: - verifier.verify(tv.msg, tv.sig) - except ValueError: - assert not tv.valid - else: - assert tv.valid - - def runTest(self): - for tv in self.tv: - self.test_sign(tv) - self.test_verify(tv) - - -def get_tests(config={}): - - tests = [] - tests += list_test_cases(TestExport_Ed25519) - tests += list_test_cases(TestExport_Ed448) - tests += list_test_cases(TestImport_Ed25519) - tests += list_test_cases(TestImport_Ed448) - tests += list_test_cases(TestEdDSA) - tests += [TestVectorsEdDSAWycheproof()] - return tests - - -if __name__ == '__main__': - def suite(): - return unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Signature/test_pkcs1_15.py b/resources/lib/deps/Cryptodome/SelfTest/Signature/test_pkcs1_15.py deleted file mode 100644 index 3a3e30b..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Signature/test_pkcs1_15.py +++ /dev/null @@ -1,348 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import json -import unittest -from binascii import unhexlify - -from Cryptodome.Util.py3compat import bchr -from Cryptodome.Util.number import bytes_to_long -from Cryptodome.Util.strxor import strxor -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.SelfTest.loader import load_test_vectors, load_test_vectors_wycheproof - -from Cryptodome.Hash import (SHA1, SHA224, SHA256, SHA384, SHA512, SHA3_384, - SHA3_224, SHA3_256, SHA3_512) -from Cryptodome.PublicKey import RSA -from Cryptodome.Signature import pkcs1_15 -from Cryptodome.Signature import PKCS1_v1_5 - -from Cryptodome.Util._file_system import pycryptodome_filename -from Cryptodome.Util.strxor import strxor - - -def load_hash_by_name(hash_name): - return __import__("Cryptodome.Hash." + hash_name, globals(), locals(), ["new"]) - - -class FIPS_PKCS1_Verify_Tests(unittest.TestCase): - - def shortDescription(self): - return "FIPS PKCS1 Tests (Verify)" - - def test_can_sign(self): - test_public_key = RSA.generate(1024).public_key() - verifier = pkcs1_15.new(test_public_key) - self.assertEqual(verifier.can_sign(), False) - - -class FIPS_PKCS1_Verify_Tests_KAT(unittest.TestCase): - pass - - -test_vectors_verify = load_test_vectors(("Signature", "PKCS1-v1.5"), - "SigVer15_186-3.rsp", - "Signature Verification 186-3", - {'shaalg': lambda x: x, - 'd': lambda x: int(x), - 'result': lambda x: x}) or [] - - -for count, tv in enumerate(test_vectors_verify): - if isinstance(tv, str): - continue - if hasattr(tv, "n"): - modulus = tv.n - continue - - hash_module = load_hash_by_name(tv.shaalg.upper()) - hash_obj = hash_module.new(tv.msg) - public_key = RSA.construct([bytes_to_long(x) for x in (modulus, tv.e)]) # type: ignore - verifier = pkcs1_15.new(public_key) - - def positive_test(self, hash_obj=hash_obj, verifier=verifier, signature=tv.s): - verifier.verify(hash_obj, signature) - - def negative_test(self, hash_obj=hash_obj, verifier=verifier, signature=tv.s): - self.assertRaises(ValueError, verifier.verify, hash_obj, signature) - - if tv.result == 'f': - setattr(FIPS_PKCS1_Verify_Tests_KAT, "test_negative_%d" % count, negative_test) - else: - setattr(FIPS_PKCS1_Verify_Tests_KAT, "test_positive_%d" % count, positive_test) - - -class FIPS_PKCS1_Sign_Tests(unittest.TestCase): - - def shortDescription(self): - return "FIPS PKCS1 Tests (Sign)" - - def test_can_sign(self): - test_private_key = RSA.generate(1024) - signer = pkcs1_15.new(test_private_key) - self.assertEqual(signer.can_sign(), True) - - -class FIPS_PKCS1_Sign_Tests_KAT(unittest.TestCase): - pass - - -test_vectors_sign = load_test_vectors(("Signature", "PKCS1-v1.5"), - "SigGen15_186-2.txt", - "Signature Generation 186-2", - {'shaalg': lambda x: x}) or [] - -test_vectors_sign += load_test_vectors(("Signature", "PKCS1-v1.5"), - "SigGen15_186-3.txt", - "Signature Generation 186-3", - {'shaalg': lambda x: x}) or [] - -for count, tv in enumerate(test_vectors_sign): - if isinstance(tv, str): - continue - if hasattr(tv, "n"): - modulus = tv.n - continue - if hasattr(tv, "e"): - private_key = RSA.construct([bytes_to_long(x) for x in (modulus, tv.e, tv.d)]) # type: ignore - signer = pkcs1_15.new(private_key) - continue - - hash_module = load_hash_by_name(tv.shaalg.upper()) - hash_obj = hash_module.new(tv.msg) - - def new_test(self, hash_obj=hash_obj, signer=signer, result=tv.s): - signature = signer.sign(hash_obj) - self.assertEqual(signature, result) - - setattr(FIPS_PKCS1_Sign_Tests_KAT, "test_%d" % count, new_test) - - -class PKCS1_15_NoParams(unittest.TestCase): - """Verify that PKCS#1 v1.5 signatures pass even without NULL parameters in - the algorithm identifier (PyCrypto/LP bug #1119552).""" - - rsakey = """-----BEGIN RSA PRIVATE KEY----- - MIIBOwIBAAJBAL8eJ5AKoIsjURpcEoGubZMxLD7+kT+TLr7UkvEtFrRhDDKMtuII - q19FrL4pUIMymPMSLBn3hJLe30Dw48GQM4UCAwEAAQJACUSDEp8RTe32ftq8IwG8 - Wojl5mAd1wFiIOrZ/Uv8b963WJOJiuQcVN29vxU5+My9GPZ7RA3hrDBEAoHUDPrI - OQIhAPIPLz4dphiD9imAkivY31Rc5AfHJiQRA7XixTcjEkojAiEAyh/pJHks/Mlr - +rdPNEpotBjfV4M4BkgGAA/ipcmaAjcCIQCHvhwwKVBLzzTscT2HeUdEeBMoiXXK - JACAr3sJQJGxIQIgarRp+m1WSKV1MciwMaTOnbU7wxFs9DP1pva76lYBzgUCIQC9 - n0CnZCJ6IZYqSt0H5N7+Q+2Ro64nuwV/OSQfM6sBwQ== - -----END RSA PRIVATE KEY-----""" - - msg = b"This is a test\x0a" - - # PKCS1 v1.5 signature of the message computed using SHA-1. - # The digestAlgorithm SEQUENCE does NOT contain the NULL parameter. - sig_str = "a287a13517f716e72fb14eea8e33a8db4a4643314607e7ca3e3e28"\ - "1893db74013dda8b855fd99f6fecedcb25fcb7a434f35cd0a101f8"\ - "b19348e0bd7b6f152dfc" - signature = unhexlify(sig_str) - - def runTest(self): - verifier = pkcs1_15.new(RSA.importKey(self.rsakey)) - hashed = SHA1.new(self.msg) - verifier.verify(hashed, self.signature) - - -class PKCS1_Legacy_Module_Tests(unittest.TestCase): - """Verify that the legacy module Cryptodome.Signature.PKCS1_v1_5 - behaves as expected. The only difference is that the verify() - method returns True/False and does not raise exceptions.""" - - def shortDescription(self): - return "Test legacy Cryptodome.Signature.PKCS1_v1_5" - - def runTest(self): - key = RSA.importKey(PKCS1_15_NoParams.rsakey) - hashed = SHA1.new(b"Test") - good_signature = PKCS1_v1_5.new(key).sign(hashed) - verifier = PKCS1_v1_5.new(key.public_key()) - - self.assertEqual(verifier.verify(hashed, good_signature), True) - - # Flip a few bits in the signature - bad_signature = strxor(good_signature, bchr(1) * len(good_signature)) - self.assertEqual(verifier.verify(hashed, bad_signature), False) - - -class PKCS1_All_Hashes_Tests(unittest.TestCase): - - def shortDescription(self): - return "Test PKCS#1v1.5 signature in combination with all hashes" - - def runTest(self): - - key = RSA.generate(1024) - signer = pkcs1_15.new(key) - hash_names = ("MD2", "MD4", "MD5", "RIPEMD160", "SHA1", - "SHA224", "SHA256", "SHA384", "SHA512", - "SHA3_224", "SHA3_256", "SHA3_384", "SHA3_512") - - for name in hash_names: - hashed = load_hash_by_name(name).new(b"Test") - signer.sign(hashed) - - from Cryptodome.Hash import BLAKE2b, BLAKE2s - for hash_size in (20, 32, 48, 64): - hashed_b = BLAKE2b.new(digest_bytes=hash_size, data=b"Test") - signer.sign(hashed_b) - for hash_size in (16, 20, 28, 32): - hashed_s = BLAKE2s.new(digest_bytes=hash_size, data=b"Test") - signer.sign(hashed_s) - - -class TestVectorsWycheproof(unittest.TestCase): - - def __init__(self, wycheproof_warnings): - unittest.TestCase.__init__(self) - self._wycheproof_warnings = wycheproof_warnings - self._id = "None" - - def setUp(self): - self.tv = [] - self.add_tests("rsa_sig_gen_misc_test.json") - self.add_tests("rsa_signature_2048_sha224_test.json") - self.add_tests("rsa_signature_2048_sha256_test.json") - self.add_tests("rsa_signature_2048_sha384_test.json") - self.add_tests("rsa_signature_2048_sha3_224_test.json") - self.add_tests("rsa_signature_2048_sha3_256_test.json") - self.add_tests("rsa_signature_2048_sha3_384_test.json") - self.add_tests("rsa_signature_2048_sha3_512_test.json") - self.add_tests("rsa_signature_2048_sha512_test.json") - self.add_tests("rsa_signature_2048_sha512_224_test.json") - self.add_tests("rsa_signature_2048_sha512_256_test.json") - self.add_tests("rsa_signature_3072_sha256_test.json") - self.add_tests("rsa_signature_3072_sha384_test.json") - self.add_tests("rsa_signature_3072_sha3_256_test.json") - self.add_tests("rsa_signature_3072_sha3_384_test.json") - self.add_tests("rsa_signature_3072_sha3_512_test.json") - self.add_tests("rsa_signature_3072_sha512_test.json") - self.add_tests("rsa_signature_3072_sha512_256_test.json") - self.add_tests("rsa_signature_4096_sha384_test.json") - self.add_tests("rsa_signature_4096_sha512_test.json") - self.add_tests("rsa_signature_4096_sha512_256_test.json") - self.add_tests("rsa_signature_test.json") - - def add_tests(self, filename): - - def filter_rsa(group): - return RSA.import_key(group['keyPem']) - - def filter_sha(group): - hash_name = group['sha'] - if hash_name == "SHA-512": - return SHA512 - elif hash_name == "SHA-512/224": - return SHA512.new(truncate="224") - elif hash_name == "SHA-512/256": - return SHA512.new(truncate="256") - elif hash_name == "SHA3-512": - return SHA3_512 - elif hash_name == "SHA-384": - return SHA384 - elif hash_name == "SHA3-384": - return SHA3_384 - elif hash_name == "SHA-256": - return SHA256 - elif hash_name == "SHA3-256": - return SHA3_256 - elif hash_name == "SHA-224": - return SHA224 - elif hash_name == "SHA3-224": - return SHA3_224 - elif hash_name == "SHA-1": - return SHA1 - else: - raise ValueError("Unknown hash algorithm: " + hash_name) - - def filter_type(group): - type_name = group['type'] - if type_name not in ("RsassaPkcs1Verify", "RsassaPkcs1Generate"): - raise ValueError("Unknown type name " + type_name) - - result = load_test_vectors_wycheproof(("Signature", "wycheproof"), - filename, - "Wycheproof PKCS#1v1.5 signature (%s)" % filename, - group_tag={'rsa_key': filter_rsa, - 'hash_mod': filter_sha, - 'type': filter_type}) - return result - - def shortDescription(self): - return self._id - - def warn(self, tv): - if tv.warning and self._wycheproof_warnings: - import warnings - warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment)) - - def test_verify(self, tv): - self._id = "Wycheproof RSA PKCS$#1 Test #" + str(tv.id) - - hashed_msg = tv.hash_module.new(tv.msg) - signer = pkcs1_15.new(tv.key) - try: - signature = signer.verify(hashed_msg, tv.sig) - except ValueError as e: - if tv.warning: - return - assert not tv.valid - else: - assert tv.valid - self.warn(tv) - - def runTest(self): - for tv in self.tv: - self.test_verify(tv) - - -def get_tests(config={}): - wycheproof_warnings = config.get('wycheproof_warnings') - - tests = [] - tests += list_test_cases(FIPS_PKCS1_Verify_Tests) - tests += list_test_cases(FIPS_PKCS1_Sign_Tests) - tests += list_test_cases(PKCS1_15_NoParams) - tests += list_test_cases(PKCS1_Legacy_Module_Tests) - tests += list_test_cases(PKCS1_All_Hashes_Tests) - tests += [ TestVectorsWycheproof(wycheproof_warnings) ] - - if config.get('slow_tests'): - tests += list_test_cases(FIPS_PKCS1_Verify_Tests_KAT) - tests += list_test_cases(FIPS_PKCS1_Sign_Tests_KAT) - - return tests - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Signature/test_pss.py b/resources/lib/deps/Cryptodome/SelfTest/Signature/test_pss.py deleted file mode 100644 index c3b1ce5..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Signature/test_pss.py +++ /dev/null @@ -1,377 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import unittest - -from Cryptodome.Util.py3compat import b, bchr -from Cryptodome.Util.number import bytes_to_long -from Cryptodome.Util.strxor import strxor -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.SelfTest.loader import load_test_vectors, load_test_vectors_wycheproof - -from Cryptodome.Hash import SHA1, SHA224, SHA256, SHA384, SHA512 -from Cryptodome.PublicKey import RSA -from Cryptodome.Signature import pss -from Cryptodome.Signature import PKCS1_PSS - -from Cryptodome.Signature.pss import MGF1 - - -def load_hash_by_name(hash_name): - return __import__("Cryptodome.Hash." + hash_name, globals(), locals(), ["new"]) - - -class PRNG(object): - - def __init__(self, stream): - self.stream = stream - self.idx = 0 - - def __call__(self, rnd_size): - result = self.stream[self.idx:self.idx + rnd_size] - self.idx += rnd_size - return result - - -class PSS_Tests(unittest.TestCase): - - rsa_key = b'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAsvI34FgiTK8+txBvmooNGpNwk23YTU51dwNZi5yha3W4lA/Q\nvcZrDalkmD7ekWQwnduxVKa6pRSI13KBgeUOIqJoGXSWhntEtY3FEwvWOHW5AE7Q\njUzTzCiYT6TVaCcpa/7YLai+p6ai2g5f5Zfh4jSawa9uYeuggFygQq4IVW796MgV\nyqxYMM/arEj+/sKz3Viua9Rp9fFosertCYCX4DUTgW0mX9bwEnEOgjSI3pLOPXz1\n8vx+DRZS5wMCmwCUa0sKonLn3cAUPq+sGix7+eo7T0Z12MU8ud7IYVX/75r3cXiF\nPaYE2q8Le0kgOApIXbb+x74x0rNgyIh1yGygkwIDAQABAoIBABz4t1A0pLT6qHI2\nEIOaNz3mwhK0dZEqkz0GB1Dhtoax5ATgvKCFB98J3lYB08IBURe1snOsnMpOVUtg\naBRSM+QqnCUG6bnzKjAkuFP5liDE+oNQv1YpKp9CsUovuzdmI8Au3ewihl+ZTIN2\nUVNYMEOR1b5m+z2SSwWNOYsiJwpBrT7zkpdlDyjat7FiiPhMMIMXjhQFVxURMIcB\njUBtPzGvV/PG90cVDWi1wRGeeP1dDqti/jsnvykQ15KW1MqGrpeNKRmDdTy/Ucl1\nWIoYklKw3U456lgZ/rDTDB818+Tlnk35z4yF7d5ANPM8CKfqOPcnO1BCKVFzf4eq\n54wvUtkCgYEA1Zv2lp06l7rXMsvNtyYQjbFChezRDRnPwZmN4NCdRtTgGG1G0Ryd\nYz6WWoPGqZp0b4LAaaHd3W2GTcpXF8WXMKfMX1W+tMAxMozfsXRKMcHoypwuS5wT\nfJRXJCG4pvd57AB0iVUEJW2we+uGKU5Zxcx//id2nXGCpoRyViIplQsCgYEA1nVC\neHupHChht0Fh4N09cGqZHZzuwXjOUMzR3Vsfz+4WzVS3NvIgN4g5YgmQFOeKwo5y\niRq5yvubcNdFvf85eHWClg0zPAyxJCVUWigCrrOanGEhJo6re4idJvNVzu4Ucg0v\n6B3SJ1HsCda+ZSNz24bSyqRep8A+RoAaoVSFx5kCgYEAn3RvXPs9s+obnqWYiPF3\nRe5etE6Vt2vfNKwFxx6zaR6bsmBQjuUHcABWiHb6I71S0bMPI0tbrWGG8ibrYKl1\nNTLtUvVVCOS3VP7oNTWT9RTFTAnOXU7DFSo+6o/poWn3r36ff6zhDXeWWMr2OXtt\ndEQ1/2lCGEGVv+v61eVmmQUCgYABFHITPTwqwiFL1O5zPWnzyPWgaovhOYSAb6eW\n38CXQXGn8wdBJZL39J2lWrr4//l45VK6UgIhfYbY2JynSkO10ZGow8RARygVMILu\nOUlaK9lZdDvAf/NpGdUAvzTtZ9F+iYZ2OsA2JnlzyzsGM1l//3vMPWukmJk3ral0\nqoJJ8QKBgGRG3eVHnIegBbFVuMDp2NTcfuSuDVUQ1fGAwtPiFa8u81IodJnMk2pq\niXu2+0ytNA/M+SVrAnE2AgIzcaJbtr0p2srkuVM7KMWnG1vWFNjtXN8fAhf/joOv\nD+NmPL/N4uE57e40tbiU/H7KdyZaDt+5QiTmdhuyAe6CBjKsF2jy\n-----END RSA PRIVATE KEY-----' - msg = b'AAA' - tag = b'\x00[c5\xd8\xb0\x8b!D\x81\x83\x07\xc0\xdd\xb9\xb4\xb2`\x92\xe7\x02\xf1\xe1P\xea\xc3\xf0\xe3>\xddX5\xdd\x8e\xc5\x89\xef\xf3\xc2\xdc\xfeP\x02\x7f\x12+\xc9\xaf\xbb\xec\xfe\xb0\xa5\xb9\x08\x11P\x8fL\xee5\x9b\xb0k{=_\xd2\x14\xfb\x01R\xb7\xfe\x14}b\x03\x8d5Y\x89~}\xfc\xf2l\xd01-\xbd\xeb\x11\xcdV\x11\xe9l\x19k/o5\xa2\x0f\x15\xe7Q$\t=\xec\x1dAB\x19\xa5P\x9a\xaf\xa3G\x86"\xd6~\xf0j\xfcqkbs\x13\x84b\xe4\xbdm(\xed`\xa4F\xfb\x8f.\xe1\x8c)/_\x9eS\x98\xa4v\xb8\xdc\xfe\xf7/D\x18\x19\xb3T\x97:\xe2\x96s\xe8<\xa2\xb4\xb9\xf8/' - - def test_positive_1(self): - key = RSA.import_key(self.rsa_key) - h = SHA256.new(self.msg) - verifier = pss.new(key) - verifier.verify(h, self.tag) - - def test_negative_1(self): - key = RSA.import_key(self.rsa_key) - h = SHA256.new(self.msg + b'A') - verifier = pss.new(key) - tag = bytearray(self.tag) - self.assertRaises(ValueError, verifier.verify, h, tag) - - def test_negative_2(self): - key = RSA.import_key(self.rsa_key) - h = SHA256.new(self.msg) - verifier = pss.new(key, salt_bytes=1000) - tag = bytearray(self.tag) - self.assertRaises(ValueError, verifier.verify, h, tag) - - -class FIPS_PKCS1_Verify_Tests(unittest.TestCase): - - def shortDescription(self): - return "FIPS PKCS1 Tests (Verify)" - - def verify_positive(self, hashmod, message, public_key, salt, signature): - prng = PRNG(salt) - hashed = hashmod.new(message) - verifier = pss.new(public_key, salt_bytes=len(salt), rand_func=prng) - verifier.verify(hashed, signature) - - def verify_negative(self, hashmod, message, public_key, salt, signature): - prng = PRNG(salt) - hashed = hashmod.new(message) - verifier = pss.new(public_key, salt_bytes=len(salt), rand_func=prng) - self.assertRaises(ValueError, verifier.verify, hashed, signature) - - def test_can_sign(self): - test_public_key = RSA.generate(1024).public_key() - verifier = pss.new(test_public_key) - self.assertEqual(verifier.can_sign(), False) - - -class FIPS_PKCS1_Verify_Tests_KAT(unittest.TestCase): - pass - - -test_vectors_verify = load_test_vectors(("Signature", "PKCS1-PSS"), - "SigVerPSS_186-3.rsp", - "Signature Verification 186-3", - {'shaalg': lambda x: x, - 'result': lambda x: x}) or [] - - -for count, tv in enumerate(test_vectors_verify): - if isinstance(tv, str): - continue - if hasattr(tv, "n"): - modulus = tv.n - continue - if hasattr(tv, "p"): - continue - - hash_module = load_hash_by_name(tv.shaalg.upper()) - hash_obj = hash_module.new(tv.msg) - public_key = RSA.construct([bytes_to_long(x) for x in (modulus, tv.e)]) # type: ignore - if tv.saltval != b("\x00"): - prng = PRNG(tv.saltval) - verifier = pss.new(public_key, salt_bytes=len(tv.saltval), rand_func=prng) - else: - verifier = pss.new(public_key, salt_bytes=0) - - def positive_test(self, hash_obj=hash_obj, verifier=verifier, signature=tv.s): - verifier.verify(hash_obj, signature) - - def negative_test(self, hash_obj=hash_obj, verifier=verifier, signature=tv.s): - self.assertRaises(ValueError, verifier.verify, hash_obj, signature) - - if tv.result == 'p': - setattr(FIPS_PKCS1_Verify_Tests_KAT, "test_positive_%d" % count, positive_test) - else: - setattr(FIPS_PKCS1_Verify_Tests_KAT, "test_negative_%d" % count, negative_test) - - -class FIPS_PKCS1_Sign_Tests(unittest.TestCase): - - def shortDescription(self): - return "FIPS PKCS1 Tests (Sign)" - - def test_can_sign(self): - test_private_key = RSA.generate(1024) - signer = pss.new(test_private_key) - self.assertEqual(signer.can_sign(), True) - - -class FIPS_PKCS1_Sign_Tests_KAT(unittest.TestCase): - pass - - -test_vectors_sign = load_test_vectors(("Signature", "PKCS1-PSS"), - "SigGenPSS_186-2.txt", - "Signature Generation 186-2", - {'shaalg': lambda x: x}) or [] - -test_vectors_sign += load_test_vectors(("Signature", "PKCS1-PSS"), - "SigGenPSS_186-3.txt", - "Signature Generation 186-3", - {'shaalg': lambda x: x}) or [] - -for count, tv in enumerate(test_vectors_sign): - if isinstance(tv, str): - continue - if hasattr(tv, "n"): - modulus = tv.n - continue - if hasattr(tv, "e"): - private_key = RSA.construct([bytes_to_long(x) for x in (modulus, tv.e, tv.d)]) # type: ignore - continue - - hash_module = load_hash_by_name(tv.shaalg.upper()) - hash_obj = hash_module.new(tv.msg) - if tv.saltval != b("\x00"): - prng = PRNG(tv.saltval) - signer = pss.new(private_key, salt_bytes=len(tv.saltval), rand_func=prng) - else: - signer = pss.new(private_key, salt_bytes=0) - - def new_test(self, hash_obj=hash_obj, signer=signer, result=tv.s): - signature = signer.sign(hash_obj) - self.assertEqual(signature, result) - - setattr(FIPS_PKCS1_Sign_Tests_KAT, "test_%d" % count, new_test) - - -class PKCS1_Legacy_Module_Tests(unittest.TestCase): - """Verify that the legacy module Cryptodome.Signature.PKCS1_PSS - behaves as expected. The only difference is that the verify() - method returns True/False and does not raise exceptions.""" - - def shortDescription(self): - return "Test legacy Cryptodome.Signature.PKCS1_PSS" - - def runTest(self): - key = RSA.generate(1024) - hashed = SHA1.new(b("Test")) - good_signature = PKCS1_PSS.new(key).sign(hashed) - verifier = PKCS1_PSS.new(key.public_key()) - - self.assertEqual(verifier.verify(hashed, good_signature), True) - - # Flip a few bits in the signature - bad_signature = strxor(good_signature, bchr(1) * len(good_signature)) - self.assertEqual(verifier.verify(hashed, bad_signature), False) - - -class PKCS1_All_Hashes_Tests(unittest.TestCase): - - def shortDescription(self): - return "Test PKCS#1 PSS signature in combination with all hashes" - - def runTest(self): - - key = RSA.generate(1280) - signer = pss.new(key) - hash_names = ("MD2", "MD4", "MD5", "RIPEMD160", "SHA1", - "SHA224", "SHA256", "SHA384", "SHA512", - "SHA3_224", "SHA3_256", "SHA3_384", "SHA3_512") - - for name in hash_names: - hashed = load_hash_by_name(name).new(b("Test")) - signer.sign(hashed) - - from Cryptodome.Hash import BLAKE2b, BLAKE2s - for hash_size in (20, 32, 48, 64): - hashed_b = BLAKE2b.new(digest_bytes=hash_size, data=b("Test")) - signer.sign(hashed_b) - for hash_size in (16, 20, 28, 32): - hashed_s = BLAKE2s.new(digest_bytes=hash_size, data=b("Test")) - signer.sign(hashed_s) - - -def get_hash_module(hash_name): - if hash_name == "SHA-512": - hash_module = SHA512 - elif hash_name == "SHA-512/224": - hash_module = SHA512.new(truncate="224") - elif hash_name == "SHA-512/256": - hash_module = SHA512.new(truncate="256") - elif hash_name == "SHA-384": - hash_module = SHA384 - elif hash_name == "SHA-256": - hash_module = SHA256 - elif hash_name == "SHA-224": - hash_module = SHA224 - elif hash_name == "SHA-1": - hash_module = SHA1 - else: - raise ValueError("Unknown hash algorithm: " + hash_name) - return hash_module - - -class TestVectorsPSSWycheproof(unittest.TestCase): - - def __init__(self, wycheproof_warnings): - unittest.TestCase.__init__(self) - self._wycheproof_warnings = wycheproof_warnings - self._id = "None" - - def add_tests(self, filename): - - def filter_rsa(group): - return RSA.import_key(group['keyPem']) - - def filter_sha(group): - return get_hash_module(group['sha']) - - def filter_type(group): - type_name = group['type'] - if type_name not in ("RsassaPssVerify", ): - raise ValueError("Unknown type name " + type_name) - - def filter_slen(group): - return group['sLen'] - - def filter_mgf(group): - mgf = group['mgf'] - if mgf not in ("MGF1", ): - raise ValueError("Unknown MGF " + mgf) - mgf1_hash = get_hash_module(group['mgfSha']) - - def mgf(x, y, mh=mgf1_hash): - return MGF1(x, y, mh) - - return mgf - - result = load_test_vectors_wycheproof(("Signature", "wycheproof"), - filename, - "Wycheproof PSS signature (%s)" % filename, - group_tag={'key': filter_rsa, - 'hash_module': filter_sha, - 'sLen': filter_slen, - 'mgf': filter_mgf, - 'type': filter_type}) - return result - - def setUp(self): - self.tv = [] - self.add_tests("rsa_pss_2048_sha1_mgf1_20_test.json") - self.add_tests("rsa_pss_2048_sha256_mgf1_0_test.json") - self.add_tests("rsa_pss_2048_sha256_mgf1_32_test.json") - self.add_tests("rsa_pss_2048_sha512_256_mgf1_28_test.json") - self.add_tests("rsa_pss_2048_sha512_256_mgf1_32_test.json") - self.add_tests("rsa_pss_3072_sha256_mgf1_32_test.json") - self.add_tests("rsa_pss_4096_sha256_mgf1_32_test.json") - self.add_tests("rsa_pss_4096_sha512_mgf1_32_test.json") - self.add_tests("rsa_pss_misc_test.json") - - def shortDescription(self): - return self._id - - def warn(self, tv): - if tv.warning and self._wycheproof_warnings: - import warnings - warnings.warn("Wycheproof warning: %s (%s)" % (self._id, tv.comment)) - - def test_verify(self, tv): - self._id = "Wycheproof RSA PSS Test #%d (%s)" % (tv.id, tv.comment) - - hashed_msg = tv.hash_module.new(tv.msg) - signer = pss.new(tv.key, mask_func=tv.mgf, salt_bytes=tv.sLen) - try: - signature = signer.verify(hashed_msg, tv.sig) - except ValueError as e: - if tv.warning: - return - assert not tv.valid - else: - assert tv.valid - self.warn(tv) - - def runTest(self): - for tv in self.tv: - self.test_verify(tv) - - -def get_tests(config={}): - wycheproof_warnings = config.get('wycheproof_warnings') - - tests = [] - tests += list_test_cases(PSS_Tests) - tests += list_test_cases(FIPS_PKCS1_Verify_Tests) - tests += list_test_cases(FIPS_PKCS1_Sign_Tests) - tests += list_test_cases(PKCS1_Legacy_Module_Tests) - tests += list_test_cases(PKCS1_All_Hashes_Tests) - - if config.get('slow_tests'): - tests += list_test_cases(FIPS_PKCS1_Verify_Tests_KAT) - tests += list_test_cases(FIPS_PKCS1_Sign_Tests_KAT) - - tests += [TestVectorsPSSWycheproof(wycheproof_warnings)] - - return tests - - -if __name__ == '__main__': - def suite(): - return unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Util/__init__.py b/resources/lib/deps/Cryptodome/SelfTest/Util/__init__.py deleted file mode 100644 index e52c490..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Util/__init__.py +++ /dev/null @@ -1,46 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Util/__init__.py: Self-test for utility modules -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test for utility modules""" - -__revision__ = "$Id$" - -import os - -def get_tests(config={}): - tests = [] - from Cryptodome.SelfTest.Util import test_number; tests += test_number.get_tests(config=config) - from Cryptodome.SelfTest.Util import test_Counter; tests += test_Counter.get_tests(config=config) - from Cryptodome.SelfTest.Util import test_Padding; tests += test_Padding.get_tests(config=config) - from Cryptodome.SelfTest.Util import test_strxor; tests += test_strxor.get_tests(config=config) - from Cryptodome.SelfTest.Util import test_asn1; tests += test_asn1.get_tests(config=config) - from Cryptodome.SelfTest.Util import test_rfc1751; tests += test_rfc1751.get_tests(config=config) - return tests - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Util/test_Counter.py b/resources/lib/deps/Cryptodome/SelfTest/Util/test_Counter.py deleted file mode 100644 index 0d1e089..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Util/test_Counter.py +++ /dev/null @@ -1,67 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Util/test_Counter: Self-test for the Cryptodome.Util.Counter module -# -# Written in 2009 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-tests for Cryptodome.Util.Counter""" - -from Cryptodome.Util.py3compat import * - -import unittest - -class CounterTests(unittest.TestCase): - def setUp(self): - global Counter - from Cryptodome.Util import Counter - - def test_BE(self): - """Big endian""" - c = Counter.new(128) - c = Counter.new(128, little_endian=False) - - def test_LE(self): - """Little endian""" - c = Counter.new(128, little_endian=True) - - def test_nbits(self): - c = Counter.new(nbits=128) - self.assertRaises(ValueError, Counter.new, 129) - - def test_prefix(self): - c = Counter.new(128, prefix=b("xx")) - - def test_suffix(self): - c = Counter.new(128, suffix=b("xx")) - - def test_iv(self): - c = Counter.new(128, initial_value=2) - self.assertRaises(ValueError, Counter.new, 16, initial_value=0x1FFFF) - -def get_tests(config={}): - from Cryptodome.SelfTest.st_common import list_test_cases - return list_test_cases(CounterTests) - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Util/test_Padding.py b/resources/lib/deps/Cryptodome/SelfTest/Util/test_Padding.py deleted file mode 100644 index d6a794e..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Util/test_Padding.py +++ /dev/null @@ -1,154 +0,0 @@ -# -# SelfTest/Util/test_Padding.py: Self-test for padding functions -# -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import unittest -from binascii import unhexlify as uh - -from Cryptodome.Util.py3compat import * -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.Util.Padding import pad, unpad - -class PKCS7_Tests(unittest.TestCase): - - def test1(self): - padded = pad(b(""), 4) - self.assertTrue(padded == uh(b("04040404"))) - padded = pad(b(""), 4, 'pkcs7') - self.assertTrue(padded == uh(b("04040404"))) - back = unpad(padded, 4) - self.assertTrue(back == b("")) - - def test2(self): - padded = pad(uh(b("12345678")), 4) - self.assertTrue(padded == uh(b("1234567804040404"))) - back = unpad(padded, 4) - self.assertTrue(back == uh(b("12345678"))) - - def test3(self): - padded = pad(uh(b("123456")), 4) - self.assertTrue(padded == uh(b("12345601"))) - back = unpad(padded, 4) - self.assertTrue(back == uh(b("123456"))) - - def test4(self): - padded = pad(uh(b("1234567890")), 4) - self.assertTrue(padded == uh(b("1234567890030303"))) - back = unpad(padded, 4) - self.assertTrue(back == uh(b("1234567890"))) - - def testn1(self): - self.assertRaises(ValueError, pad, uh(b("12")), 4, 'pkcs8') - - def testn2(self): - self.assertRaises(ValueError, unpad, b("\0\0\0"), 4) - self.assertRaises(ValueError, unpad, b(""), 4) - - def testn3(self): - self.assertRaises(ValueError, unpad, b("123456\x02"), 4) - self.assertRaises(ValueError, unpad, b("123456\x00"), 4) - self.assertRaises(ValueError, unpad, b("123456\x05\x05\x05\x05\x05"), 4) - -class X923_Tests(unittest.TestCase): - - def test1(self): - padded = pad(b(""), 4, 'x923') - self.assertTrue(padded == uh(b("00000004"))) - back = unpad(padded, 4, 'x923') - self.assertTrue(back == b("")) - - def test2(self): - padded = pad(uh(b("12345678")), 4, 'x923') - self.assertTrue(padded == uh(b("1234567800000004"))) - back = unpad(padded, 4, 'x923') - self.assertTrue(back == uh(b("12345678"))) - - def test3(self): - padded = pad(uh(b("123456")), 4, 'x923') - self.assertTrue(padded == uh(b("12345601"))) - back = unpad(padded, 4, 'x923') - self.assertTrue(back == uh(b("123456"))) - - def test4(self): - padded = pad(uh(b("1234567890")), 4, 'x923') - self.assertTrue(padded == uh(b("1234567890000003"))) - back = unpad(padded, 4, 'x923') - self.assertTrue(back == uh(b("1234567890"))) - - def testn1(self): - self.assertRaises(ValueError, unpad, b("123456\x02"), 4, 'x923') - self.assertRaises(ValueError, unpad, b("123456\x00"), 4, 'x923') - self.assertRaises(ValueError, unpad, b("123456\x00\x00\x00\x00\x05"), 4, 'x923') - self.assertRaises(ValueError, unpad, b(""), 4, 'x923') - -class ISO7816_Tests(unittest.TestCase): - - def test1(self): - padded = pad(b(""), 4, 'iso7816') - self.assertTrue(padded == uh(b("80000000"))) - back = unpad(padded, 4, 'iso7816') - self.assertTrue(back == b("")) - - def test2(self): - padded = pad(uh(b("12345678")), 4, 'iso7816') - self.assertTrue(padded == uh(b("1234567880000000"))) - back = unpad(padded, 4, 'iso7816') - self.assertTrue(back == uh(b("12345678"))) - - def test3(self): - padded = pad(uh(b("123456")), 4, 'iso7816') - self.assertTrue(padded == uh(b("12345680"))) - #import pdb; pdb.set_trace() - back = unpad(padded, 4, 'iso7816') - self.assertTrue(back == uh(b("123456"))) - - def test4(self): - padded = pad(uh(b("1234567890")), 4, 'iso7816') - self.assertTrue(padded == uh(b("1234567890800000"))) - back = unpad(padded, 4, 'iso7816') - self.assertTrue(back == uh(b("1234567890"))) - - def testn1(self): - self.assertRaises(ValueError, unpad, b("123456\x81"), 4, 'iso7816') - self.assertRaises(ValueError, unpad, b(""), 4, 'iso7816') - -def get_tests(config={}): - tests = [] - tests += list_test_cases(PKCS7_Tests) - tests += list_test_cases(X923_Tests) - tests += list_test_cases(ISO7816_Tests) - return tests - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - diff --git a/resources/lib/deps/Cryptodome/SelfTest/Util/test_asn1.py b/resources/lib/deps/Cryptodome/SelfTest/Util/test_asn1.py deleted file mode 100644 index 811ac84..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Util/test_asn1.py +++ /dev/null @@ -1,851 +0,0 @@ -# -# SelfTest/Util/test_asn.py: Self-test for the Cryptodome.Util.asn1 module -# -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -"""Self-tests for Cryptodome.Util.asn1""" - -import unittest - -from Cryptodome.Util.py3compat import * -from Cryptodome.Util.asn1 import (DerObject, DerSetOf, DerInteger, - DerBitString, - DerObjectId, DerNull, DerOctetString, - DerSequence, DerBoolean) - -class DerObjectTests(unittest.TestCase): - - def testObjInit1(self): - # Fail with invalid tag format (must be 1 byte) - self.assertRaises(ValueError, DerObject, b('\x00\x99')) - # Fail with invalid implicit tag (must be <0x1F) - self.assertRaises(ValueError, DerObject, 0x1F) - - # ------ - - def testObjEncode1(self): - # No payload - der = DerObject(b('\x02')) - self.assertEqual(der.encode(), b('\x02\x00')) - # Small payload (primitive) - der.payload = b('\x45') - self.assertEqual(der.encode(), b('\x02\x01\x45')) - # Invariant - self.assertEqual(der.encode(), b('\x02\x01\x45')) - # Initialize with numerical tag - der = DerObject(0x04) - der.payload = b('\x45') - self.assertEqual(der.encode(), b('\x04\x01\x45')) - # Initialize with constructed type - der = DerObject(b('\x10'), constructed=True) - self.assertEqual(der.encode(), b('\x30\x00')) - - def testObjEncode2(self): - # Initialize with payload - der = DerObject(0x03, b('\x12\x12')) - self.assertEqual(der.encode(), b('\x03\x02\x12\x12')) - - def testObjEncode3(self): - # Long payload - der = DerObject(b('\x10')) - der.payload = b("0")*128 - self.assertEqual(der.encode(), b('\x10\x81\x80' + "0"*128)) - - def testObjEncode4(self): - # Implicit tags (constructed) - der = DerObject(0x10, implicit=1, constructed=True) - der.payload = b('ppll') - self.assertEqual(der.encode(), b('\xa1\x04ppll')) - # Implicit tags (primitive) - der = DerObject(0x02, implicit=0x1E, constructed=False) - der.payload = b('ppll') - self.assertEqual(der.encode(), b('\x9E\x04ppll')) - - def testObjEncode5(self): - # Encode type with explicit tag - der = DerObject(0x10, explicit=5) - der.payload = b("xxll") - self.assertEqual(der.encode(), b("\xa5\x06\x10\x04xxll")) - - # ----- - - def testObjDecode1(self): - # Decode short payload - der = DerObject(0x02) - der.decode(b('\x02\x02\x01\x02')) - self.assertEqual(der.payload, b("\x01\x02")) - self.assertEqual(der._tag_octet, 0x02) - - def testObjDecode2(self): - # Decode long payload - der = DerObject(0x02) - der.decode(b('\x02\x81\x80' + "1"*128)) - self.assertEqual(der.payload, b("1")*128) - self.assertEqual(der._tag_octet, 0x02) - - def testObjDecode3(self): - # Decode payload with too much data gives error - der = DerObject(0x02) - self.assertRaises(ValueError, der.decode, b('\x02\x02\x01\x02\xFF')) - # Decode payload with too little data gives error - der = DerObject(0x02) - self.assertRaises(ValueError, der.decode, b('\x02\x02\x01')) - - def testObjDecode4(self): - # Decode implicit tag (primitive) - der = DerObject(0x02, constructed=False, implicit=0xF) - self.assertRaises(ValueError, der.decode, b('\x02\x02\x01\x02')) - der.decode(b('\x8F\x01\x00')) - self.assertEqual(der.payload, b('\x00')) - # Decode implicit tag (constructed) - der = DerObject(0x02, constructed=True, implicit=0xF) - self.assertRaises(ValueError, der.decode, b('\x02\x02\x01\x02')) - der.decode(b('\xAF\x01\x00')) - self.assertEqual(der.payload, b('\x00')) - - def testObjDecode5(self): - # Decode payload with unexpected tag gives error - der = DerObject(0x02) - self.assertRaises(ValueError, der.decode, b('\x03\x02\x01\x02')) - - def testObjDecode6(self): - # Arbitrary DER object - der = DerObject() - der.decode(b('\x65\x01\x88')) - self.assertEqual(der._tag_octet, 0x65) - self.assertEqual(der.payload, b('\x88')) - - def testObjDecode7(self): - # Decode explicit tag - der = DerObject(0x10, explicit=5) - der.decode(b("\xa5\x06\x10\x04xxll")) - self.assertEqual(der._inner_tag_octet, 0x10) - self.assertEqual(der.payload, b('xxll')) - - # Explicit tag may be 0 - der = DerObject(0x10, explicit=0) - der.decode(b("\xa0\x06\x10\x04xxll")) - self.assertEqual(der._inner_tag_octet, 0x10) - self.assertEqual(der.payload, b('xxll')) - - def testObjDecode8(self): - # Verify that decode returns the object - der = DerObject(0x02) - self.assertEqual(der, der.decode(b('\x02\x02\x01\x02'))) - -class DerIntegerTests(unittest.TestCase): - - def testInit1(self): - der = DerInteger(1) - self.assertEqual(der.encode(), b('\x02\x01\x01')) - - def testEncode1(self): - # Single-byte integers - # Value 0 - der = DerInteger(0) - self.assertEqual(der.encode(), b('\x02\x01\x00')) - # Value 1 - der = DerInteger(1) - self.assertEqual(der.encode(), b('\x02\x01\x01')) - # Value 127 - der = DerInteger(127) - self.assertEqual(der.encode(), b('\x02\x01\x7F')) - - def testEncode2(self): - # Multi-byte integers - # Value 128 - der = DerInteger(128) - self.assertEqual(der.encode(), b('\x02\x02\x00\x80')) - # Value 0x180 - der = DerInteger(0x180) - self.assertEqual(der.encode(), b('\x02\x02\x01\x80')) - # One very long integer - der = DerInteger(2**2048) - self.assertEqual(der.encode(), - b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) - - def testEncode3(self): - # Negative integers - # Value -1 - der = DerInteger(-1) - self.assertEqual(der.encode(), b('\x02\x01\xFF')) - # Value -128 - der = DerInteger(-128) - self.assertEqual(der.encode(), b('\x02\x01\x80')) - # Value - der = DerInteger(-87873) - self.assertEqual(der.encode(), b('\x02\x03\xFE\xA8\xBF')) - - def testEncode4(self): - # Explicit encoding - number = DerInteger(0x34, explicit=3) - self.assertEqual(number.encode(), b('\xa3\x03\x02\x01\x34')) - - # ----- - - def testDecode1(self): - # Single-byte integer - der = DerInteger() - # Value 0 - der.decode(b('\x02\x01\x00')) - self.assertEqual(der.value, 0) - # Value 1 - der.decode(b('\x02\x01\x01')) - self.assertEqual(der.value, 1) - # Value 127 - der.decode(b('\x02\x01\x7F')) - self.assertEqual(der.value, 127) - - def testDecode2(self): - # Multi-byte integer - der = DerInteger() - # Value 0x180L - der.decode(b('\x02\x02\x01\x80')) - self.assertEqual(der.value,0x180) - # One very long integer - der.decode( - b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) - self.assertEqual(der.value,2**2048) - - def testDecode3(self): - # Negative integer - der = DerInteger() - # Value -1 - der.decode(b('\x02\x01\xFF')) - self.assertEqual(der.value, -1) - # Value -32768 - der.decode(b('\x02\x02\x80\x00')) - self.assertEqual(der.value, -32768) - - def testDecode5(self): - # We still accept BER integer format - der = DerInteger() - # Redundant leading zeroes - der.decode(b('\x02\x02\x00\x01')) - self.assertEqual(der.value, 1) - # Redundant leading 0xFF - der.decode(b('\x02\x02\xFF\xFF')) - self.assertEqual(der.value, -1) - # Empty payload - der.decode(b('\x02\x00')) - self.assertEqual(der.value, 0) - - def testDecode6(self): - # Explicit encoding - number = DerInteger(explicit=3) - number.decode(b('\xa3\x03\x02\x01\x34')) - self.assertEqual(number.value, 0x34) - - def testDecode7(self): - # Verify decode returns the DerInteger - der = DerInteger() - self.assertEqual(der, der.decode(b('\x02\x01\x7F'))) - - ### - - def testStrict1(self): - number = DerInteger() - - number.decode(b'\x02\x02\x00\x01') - number.decode(b'\x02\x02\x00\x7F') - self.assertRaises(ValueError, number.decode, b'\x02\x02\x00\x01', strict=True) - self.assertRaises(ValueError, number.decode, b'\x02\x02\x00\x7F', strict=True) - - ### - - def testErrDecode1(self): - # Wide length field - der = DerInteger() - self.assertRaises(ValueError, der.decode, b('\x02\x81\x01\x01')) - - -class DerSequenceTests(unittest.TestCase): - - def testInit1(self): - der = DerSequence([1, DerInteger(2), b('0\x00')]) - self.assertEqual(der.encode(), b('0\x08\x02\x01\x01\x02\x01\x020\x00')) - - def testEncode1(self): - # Empty sequence - der = DerSequence() - self.assertEqual(der.encode(), b('0\x00')) - self.assertFalse(der.hasOnlyInts()) - # One single-byte integer (zero) - der.append(0) - self.assertEqual(der.encode(), b('0\x03\x02\x01\x00')) - self.assertEqual(der.hasInts(),1) - self.assertEqual(der.hasInts(False),1) - self.assertTrue(der.hasOnlyInts()) - self.assertTrue(der.hasOnlyInts(False)) - # Invariant - self.assertEqual(der.encode(), b('0\x03\x02\x01\x00')) - - def testEncode2(self): - # Indexing - der = DerSequence() - der.append(0) - der[0] = 1 - self.assertEqual(len(der),1) - self.assertEqual(der[0],1) - self.assertEqual(der[-1],1) - self.assertEqual(der.encode(), b('0\x03\x02\x01\x01')) - # - der[:] = [1] - self.assertEqual(len(der),1) - self.assertEqual(der[0],1) - self.assertEqual(der.encode(), b('0\x03\x02\x01\x01')) - - def testEncode3(self): - # One multi-byte integer (non-zero) - der = DerSequence() - der.append(0x180) - self.assertEqual(der.encode(), b('0\x04\x02\x02\x01\x80')) - - def testEncode4(self): - # One very long integer - der = DerSequence() - der.append(2**2048) - self.assertEqual(der.encode(), b('0\x82\x01\x05')+ - b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) - - def testEncode5(self): - der = DerSequence() - der += 1 - der += b('\x30\x00') - self.assertEqual(der.encode(), b('\x30\x05\x02\x01\x01\x30\x00')) - - def testEncode6(self): - # Two positive integers - der = DerSequence() - der.append(0x180) - der.append(0xFF) - self.assertEqual(der.encode(), b('0\x08\x02\x02\x01\x80\x02\x02\x00\xff')) - self.assertTrue(der.hasOnlyInts()) - self.assertTrue(der.hasOnlyInts(False)) - # Two mixed integers - der = DerSequence() - der.append(2) - der.append(-2) - self.assertEqual(der.encode(), b('0\x06\x02\x01\x02\x02\x01\xFE')) - self.assertEqual(der.hasInts(), 1) - self.assertEqual(der.hasInts(False), 2) - self.assertFalse(der.hasOnlyInts()) - self.assertTrue(der.hasOnlyInts(False)) - # - der.append(0x01) - der[1:] = [9,8] - self.assertEqual(len(der),3) - self.assertEqual(der[1:],[9,8]) - self.assertEqual(der[1:-1],[9]) - self.assertEqual(der.encode(), b('0\x09\x02\x01\x02\x02\x01\x09\x02\x01\x08')) - - def testEncode7(self): - # One integer and another type (already encoded) - der = DerSequence() - der.append(0x180) - der.append(b('0\x03\x02\x01\x05')) - self.assertEqual(der.encode(), b('0\x09\x02\x02\x01\x800\x03\x02\x01\x05')) - self.assertFalse(der.hasOnlyInts()) - - def testEncode8(self): - # One integer and another type (yet to encode) - der = DerSequence() - der.append(0x180) - der.append(DerSequence([5])) - self.assertEqual(der.encode(), b('0\x09\x02\x02\x01\x800\x03\x02\x01\x05')) - self.assertFalse(der.hasOnlyInts()) - - #### - - def testDecode1(self): - # Empty sequence - der = DerSequence() - der.decode(b('0\x00')) - self.assertEqual(len(der),0) - # One single-byte integer (zero) - der.decode(b('0\x03\x02\x01\x00')) - self.assertEqual(len(der),1) - self.assertEqual(der[0],0) - # Invariant - der.decode(b('0\x03\x02\x01\x00')) - self.assertEqual(len(der),1) - self.assertEqual(der[0],0) - - def testDecode2(self): - # One single-byte integer (non-zero) - der = DerSequence() - der.decode(b('0\x03\x02\x01\x7f')) - self.assertEqual(len(der),1) - self.assertEqual(der[0],127) - - def testDecode4(self): - # One very long integer - der = DerSequence() - der.decode(b('0\x82\x01\x05')+ - b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) - self.assertEqual(len(der),1) - self.assertEqual(der[0],2**2048) - - def testDecode6(self): - # Two integers - der = DerSequence() - der.decode(b('0\x08\x02\x02\x01\x80\x02\x02\x00\xff')) - self.assertEqual(len(der),2) - self.assertEqual(der[0],0x180) - self.assertEqual(der[1],0xFF) - - def testDecode7(self): - # One integer and 2 other types - der = DerSequence() - der.decode(b('0\x0A\x02\x02\x01\x80\x24\x02\xb6\x63\x12\x00')) - self.assertEqual(len(der),3) - self.assertEqual(der[0],0x180) - self.assertEqual(der[1],b('\x24\x02\xb6\x63')) - self.assertEqual(der[2],b('\x12\x00')) - - def testDecode8(self): - # Only 2 other types - der = DerSequence() - der.decode(b('0\x06\x24\x02\xb6\x63\x12\x00')) - self.assertEqual(len(der),2) - self.assertEqual(der[0],b('\x24\x02\xb6\x63')) - self.assertEqual(der[1],b('\x12\x00')) - self.assertEqual(der.hasInts(), 0) - self.assertEqual(der.hasInts(False), 0) - self.assertFalse(der.hasOnlyInts()) - self.assertFalse(der.hasOnlyInts(False)) - - def testDecode9(self): - # Verify that decode returns itself - der = DerSequence() - self.assertEqual(der, der.decode(b('0\x06\x24\x02\xb6\x63\x12\x00'))) - - ### - - def testErrDecode1(self): - # Not a sequence - der = DerSequence() - self.assertRaises(ValueError, der.decode, b('')) - self.assertRaises(ValueError, der.decode, b('\x00')) - self.assertRaises(ValueError, der.decode, b('\x30')) - - def testErrDecode2(self): - der = DerSequence() - # Too much data - self.assertRaises(ValueError, der.decode, b('\x30\x00\x00')) - - def testErrDecode3(self): - # Wrong length format - der = DerSequence() - # Missing length in sub-item - self.assertRaises(ValueError, der.decode, b('\x30\x04\x02\x01\x01\x00')) - # Valid BER, but invalid DER length - self.assertRaises(ValueError, der.decode, b('\x30\x81\x03\x02\x01\x01')) - self.assertRaises(ValueError, der.decode, b('\x30\x04\x02\x81\x01\x01')) - - def test_expected_nr_elements(self): - der_bin = DerSequence([1, 2, 3]).encode() - - DerSequence().decode(der_bin, nr_elements=3) - DerSequence().decode(der_bin, nr_elements=(2,3)) - self.assertRaises(ValueError, DerSequence().decode, der_bin, nr_elements=1) - self.assertRaises(ValueError, DerSequence().decode, der_bin, nr_elements=(4,5)) - - def test_expected_only_integers(self): - - der_bin1 = DerSequence([1, 2, 3]).encode() - der_bin2 = DerSequence([1, 2, DerSequence([3, 4])]).encode() - - DerSequence().decode(der_bin1, only_ints_expected=True) - DerSequence().decode(der_bin1, only_ints_expected=False) - DerSequence().decode(der_bin2, only_ints_expected=False) - self.assertRaises(ValueError, DerSequence().decode, der_bin2, only_ints_expected=True) - - -class DerOctetStringTests(unittest.TestCase): - - def testInit1(self): - der = DerOctetString(b('\xFF')) - self.assertEqual(der.encode(), b('\x04\x01\xFF')) - - def testEncode1(self): - # Empty sequence - der = DerOctetString() - self.assertEqual(der.encode(), b('\x04\x00')) - # Small payload - der.payload = b('\x01\x02') - self.assertEqual(der.encode(), b('\x04\x02\x01\x02')) - - #### - - def testDecode1(self): - # Empty sequence - der = DerOctetString() - der.decode(b('\x04\x00')) - self.assertEqual(der.payload, b('')) - # Small payload - der.decode(b('\x04\x02\x01\x02')) - self.assertEqual(der.payload, b('\x01\x02')) - - def testDecode2(self): - # Verify that decode returns the object - der = DerOctetString() - self.assertEqual(der, der.decode(b('\x04\x00'))) - - def testErrDecode1(self): - # No leftovers allowed - der = DerOctetString() - self.assertRaises(ValueError, der.decode, b('\x04\x01\x01\xff')) - -class DerNullTests(unittest.TestCase): - - def testEncode1(self): - der = DerNull() - self.assertEqual(der.encode(), b('\x05\x00')) - - #### - - def testDecode1(self): - # Empty sequence - der = DerNull() - self.assertEqual(der, der.decode(b('\x05\x00'))) - -class DerObjectIdTests(unittest.TestCase): - - def testInit1(self): - der = DerObjectId("1.1") - self.assertEqual(der.encode(), b'\x06\x01)') - - def testEncode1(self): - der = DerObjectId('1.2.840.113549.1.1.1') - self.assertEqual(der.encode(), b'\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01') - - der = DerObjectId() - der.value = '1.2.840.113549.1.1.1' - self.assertEqual(der.encode(), b'\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01') - - der = DerObjectId('2.999.1234') - self.assertEqual(der.encode(), b'\x06\x04\x88\x37\x89\x52') - - def testEncode2(self): - der = DerObjectId('3.4') - self.assertRaises(ValueError, der.encode) - - der = DerObjectId('1.40') - self.assertRaises(ValueError, der.encode) - - #### - - def testDecode1(self): - # Empty sequence - der = DerObjectId() - der.decode(b'\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01') - self.assertEqual(der.value, '1.2.840.113549.1.1.1') - - def testDecode2(self): - # Verify that decode returns the object - der = DerObjectId() - self.assertEqual(der, - der.decode(b'\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01')) - - def testDecode3(self): - der = DerObjectId() - der.decode(b'\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x00\x01') - self.assertEqual(der.value, '1.2.840.113549.1.0.1') - - def testDecode4(self): - der = DerObjectId() - der.decode(b'\x06\x04\x88\x37\x89\x52') - self.assertEqual(der.value, '2.999.1234') - - -class DerBitStringTests(unittest.TestCase): - - def testInit1(self): - der = DerBitString(b("\xFF")) - self.assertEqual(der.encode(), b('\x03\x02\x00\xFF')) - - def testInit2(self): - der = DerBitString(DerInteger(1)) - self.assertEqual(der.encode(), b('\x03\x04\x00\x02\x01\x01')) - - def testEncode1(self): - # Empty sequence - der = DerBitString() - self.assertEqual(der.encode(), b('\x03\x01\x00')) - # Small payload - der = DerBitString(b('\x01\x02')) - self.assertEqual(der.encode(), b('\x03\x03\x00\x01\x02')) - # Small payload - der = DerBitString() - der.value = b('\x01\x02') - self.assertEqual(der.encode(), b('\x03\x03\x00\x01\x02')) - - #### - - def testDecode1(self): - # Empty sequence - der = DerBitString() - der.decode(b('\x03\x00')) - self.assertEqual(der.value, b('')) - # Small payload - der.decode(b('\x03\x03\x00\x01\x02')) - self.assertEqual(der.value, b('\x01\x02')) - - def testDecode2(self): - # Verify that decode returns the object - der = DerBitString() - self.assertEqual(der, der.decode(b('\x03\x00'))) - - -class DerSetOfTests(unittest.TestCase): - - def testInit1(self): - der = DerSetOf([DerInteger(1), DerInteger(2)]) - self.assertEqual(der.encode(), b('1\x06\x02\x01\x01\x02\x01\x02')) - - def testEncode1(self): - # Empty set - der = DerSetOf() - self.assertEqual(der.encode(), b('1\x00')) - # One single-byte integer (zero) - der.add(0) - self.assertEqual(der.encode(), b('1\x03\x02\x01\x00')) - # Invariant - self.assertEqual(der.encode(), b('1\x03\x02\x01\x00')) - - def testEncode2(self): - # Two integers - der = DerSetOf() - der.add(0x180) - der.add(0xFF) - self.assertEqual(der.encode(), b('1\x08\x02\x02\x00\xff\x02\x02\x01\x80')) - # Initialize with integers - der = DerSetOf([0x180, 0xFF]) - self.assertEqual(der.encode(), b('1\x08\x02\x02\x00\xff\x02\x02\x01\x80')) - - def testEncode3(self): - # One integer and another type (no matter what it is) - der = DerSetOf() - der.add(0x180) - self.assertRaises(ValueError, der.add, b('\x00\x02\x00\x00')) - - def testEncode4(self): - # Only non integers - der = DerSetOf() - der.add(b('\x01\x00')) - der.add(b('\x01\x01\x01')) - self.assertEqual(der.encode(), b('1\x05\x01\x00\x01\x01\x01')) - - #### - - def testDecode1(self): - # Empty sequence - der = DerSetOf() - der.decode(b('1\x00')) - self.assertEqual(len(der),0) - # One single-byte integer (zero) - der.decode(b('1\x03\x02\x01\x00')) - self.assertEqual(len(der),1) - self.assertEqual(list(der),[0]) - - def testDecode2(self): - # Two integers - der = DerSetOf() - der.decode(b('1\x08\x02\x02\x01\x80\x02\x02\x00\xff')) - self.assertEqual(len(der),2) - l = list(der) - self.assertTrue(0x180 in l) - self.assertTrue(0xFF in l) - - def testDecode3(self): - # One integer and 2 other types - der = DerSetOf() - #import pdb; pdb.set_trace() - self.assertRaises(ValueError, der.decode, - b('0\x0A\x02\x02\x01\x80\x24\x02\xb6\x63\x12\x00')) - - def testDecode4(self): - # Verify that decode returns the object - der = DerSetOf() - self.assertEqual(der, - der.decode(b('1\x08\x02\x02\x01\x80\x02\x02\x00\xff'))) - - ### - - def testErrDecode1(self): - # No leftovers allowed - der = DerSetOf() - self.assertRaises(ValueError, der.decode, - b('1\x08\x02\x02\x01\x80\x02\x02\x00\xff\xAA')) - - -class DerBooleanTests(unittest.TestCase): - - def testEncode1(self): - der = DerBoolean(False) - self.assertEqual(der.encode(), b'\x01\x01\x00') - - def testEncode2(self): - der = DerBoolean(True) - self.assertEqual(der.encode(), b'\x01\x01\xFF') - - def testEncode3(self): - der = DerBoolean(False, implicit=0x12) - self.assertEqual(der.encode(), b'\x92\x01\x00') - - def testEncode4(self): - der = DerBoolean(False, explicit=0x05) - self.assertEqual(der.encode(), b'\xA5\x03\x01\x01\x00') - #### - - def testDecode1(self): - der = DerBoolean() - der.decode(b'\x01\x01\x00') - self.assertEqual(der.value, False) - - def testDecode2(self): - der = DerBoolean() - der.decode(b'\x01\x01\xFF') - self.assertEqual(der.value, True) - - def testDecode3(self): - der = DerBoolean(implicit=0x12) - der.decode(b'\x92\x01\x00') - self.assertEqual(der.value, False) - - def testDecode4(self): - der = DerBoolean(explicit=0x05) - der.decode(b'\xA5\x03\x01\x01\x00') - self.assertEqual(der.value, False) - - def testErrorDecode1(self): - der = DerBoolean() - # Wrong tag - self.assertRaises(ValueError, der.decode, b'\x02\x01\x00') - - def testErrorDecode2(self): - der = DerBoolean() - # Payload too long - self.assertRaises(ValueError, der.decode, b'\x01\x01\x00\xFF') - - -def get_tests(config={}): - from Cryptodome.SelfTest.st_common import list_test_cases - listTests = [] - listTests += list_test_cases(DerObjectTests) - listTests += list_test_cases(DerIntegerTests) - listTests += list_test_cases(DerSequenceTests) - listTests += list_test_cases(DerOctetStringTests) - listTests += list_test_cases(DerNullTests) - listTests += list_test_cases(DerObjectIdTests) - listTests += list_test_cases(DerBitStringTests) - listTests += list_test_cases(DerSetOfTests) - listTests += list_test_cases(DerBooleanTests) - return listTests - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Util/test_number.py b/resources/lib/deps/Cryptodome/SelfTest/Util/test_number.py deleted file mode 100644 index 8221443..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Util/test_number.py +++ /dev/null @@ -1,192 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Util/test_number.py: Self-test for parts of the Cryptodome.Util.number module -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-tests for (some of) Cryptodome.Util.number""" - -import math -import unittest - -from Cryptodome.Util.py3compat import * -from Cryptodome.SelfTest.st_common import list_test_cases - -from Cryptodome.Util import number -from Cryptodome.Util.number import long_to_bytes - - -class MyError(Exception): - """Dummy exception used for tests""" - -# NB: In some places, we compare tuples instead of just output values so that -# if any inputs cause a test failure, we'll be able to tell which ones. - -class MiscTests(unittest.TestCase): - - def test_ceil_div(self): - """Util.number.ceil_div""" - self.assertRaises(TypeError, number.ceil_div, "1", 1) - self.assertRaises(ZeroDivisionError, number.ceil_div, 1, 0) - self.assertRaises(ZeroDivisionError, number.ceil_div, -1, 0) - - # b = 1 - self.assertEqual(0, number.ceil_div(0, 1)) - self.assertEqual(1, number.ceil_div(1, 1)) - self.assertEqual(2, number.ceil_div(2, 1)) - self.assertEqual(3, number.ceil_div(3, 1)) - - # b = 2 - self.assertEqual(0, number.ceil_div(0, 2)) - self.assertEqual(1, number.ceil_div(1, 2)) - self.assertEqual(1, number.ceil_div(2, 2)) - self.assertEqual(2, number.ceil_div(3, 2)) - self.assertEqual(2, number.ceil_div(4, 2)) - self.assertEqual(3, number.ceil_div(5, 2)) - - # b = 3 - self.assertEqual(0, number.ceil_div(0, 3)) - self.assertEqual(1, number.ceil_div(1, 3)) - self.assertEqual(1, number.ceil_div(2, 3)) - self.assertEqual(1, number.ceil_div(3, 3)) - self.assertEqual(2, number.ceil_div(4, 3)) - self.assertEqual(2, number.ceil_div(5, 3)) - self.assertEqual(2, number.ceil_div(6, 3)) - self.assertEqual(3, number.ceil_div(7, 3)) - - # b = 4 - self.assertEqual(0, number.ceil_div(0, 4)) - self.assertEqual(1, number.ceil_div(1, 4)) - self.assertEqual(1, number.ceil_div(2, 4)) - self.assertEqual(1, number.ceil_div(3, 4)) - self.assertEqual(1, number.ceil_div(4, 4)) - self.assertEqual(2, number.ceil_div(5, 4)) - self.assertEqual(2, number.ceil_div(6, 4)) - self.assertEqual(2, number.ceil_div(7, 4)) - self.assertEqual(2, number.ceil_div(8, 4)) - self.assertEqual(3, number.ceil_div(9, 4)) - - def test_getPrime(self): - """Util.number.getPrime""" - self.assertRaises(ValueError, number.getPrime, -100) - self.assertRaises(ValueError, number.getPrime, 0) - self.assertRaises(ValueError, number.getPrime, 1) - - bits = 4 - for i in range(100): - x = number.getPrime(bits) - self.assertEqual(x >= (1 << bits - 1), 1) - self.assertEqual(x < (1 << bits), 1) - - bits = 512 - x = number.getPrime(bits) - self.assertNotEqual(x % 2, 0) - self.assertEqual(x >= (1 << bits - 1), 1) - self.assertEqual(x < (1 << bits), 1) - - def test_getStrongPrime(self): - """Util.number.getStrongPrime""" - self.assertRaises(ValueError, number.getStrongPrime, 256) - self.assertRaises(ValueError, number.getStrongPrime, 513) - bits = 512 - x = number.getStrongPrime(bits) - self.assertNotEqual(x % 2, 0) - self.assertEqual(x > (1 << bits-1)-1, 1) - self.assertEqual(x < (1 << bits), 1) - e = 2**16+1 - x = number.getStrongPrime(bits, e) - self.assertEqual(number.GCD(x-1, e), 1) - self.assertNotEqual(x % 2, 0) - self.assertEqual(x > (1 << bits-1)-1, 1) - self.assertEqual(x < (1 << bits), 1) - e = 2**16+2 - x = number.getStrongPrime(bits, e) - self.assertEqual(number.GCD((x-1)>>1, e), 1) - self.assertNotEqual(x % 2, 0) - self.assertEqual(x > (1 << bits-1)-1, 1) - self.assertEqual(x < (1 << bits), 1) - - def test_isPrime(self): - """Util.number.isPrime""" - self.assertEqual(number.isPrime(-3), False) # Regression test: negative numbers should not be prime - self.assertEqual(number.isPrime(-2), False) # Regression test: negative numbers should not be prime - self.assertEqual(number.isPrime(1), False) # Regression test: isPrime(1) caused some versions of PyCryptodome to crash. - self.assertEqual(number.isPrime(2), True) - self.assertEqual(number.isPrime(3), True) - self.assertEqual(number.isPrime(4), False) - self.assertEqual(number.isPrime(2**1279-1), True) - self.assertEqual(number.isPrime(-(2**1279-1)), False) # Regression test: negative numbers should not be prime - # test some known gmp pseudo-primes taken from - # http://www.trnicely.net/misc/mpzspsp.html - for composite in (43 * 127 * 211, 61 * 151 * 211, 15259 * 30517, - 346141 * 692281, 1007119 * 2014237, 3589477 * 7178953, - 4859419 * 9718837, 2730439 * 5460877, - 245127919 * 490255837, 963939391 * 1927878781, - 4186358431 * 8372716861, 1576820467 * 3153640933): - self.assertEqual(number.isPrime(int(composite)), False) - - def test_size(self): - self.assertEqual(number.size(2),2) - self.assertEqual(number.size(3),2) - self.assertEqual(number.size(0xa2),8) - self.assertEqual(number.size(0xa2ba40),8*3) - self.assertEqual(number.size(0xa2ba40ee07e3b2bd2f02ce227f36a195024486e49c19cb41bbbdfbba98b22b0e577c2eeaffa20d883a76e65e394c69d4b3c05a1e8fadda27edb2a42bc000fe888b9b32c22d15add0cd76b3e7936e19955b220dd17d4ea904b1ec102b2e4de7751222aa99151024c7cb41cc5ea21d00eeb41f7c800834d2c6e06bce3bce7ea9a5), 1024) - self.assertRaises(ValueError, number.size, -1) - - -class LongTests(unittest.TestCase): - - def test1(self): - self.assertEqual(long_to_bytes(0), b'\x00') - self.assertEqual(long_to_bytes(1), b'\x01') - self.assertEqual(long_to_bytes(0x100), b'\x01\x00') - self.assertEqual(long_to_bytes(0xFF00000000), b'\xFF\x00\x00\x00\x00') - self.assertEqual(long_to_bytes(0xFF00000000), b'\xFF\x00\x00\x00\x00') - self.assertEqual(long_to_bytes(0x1122334455667788), b'\x11\x22\x33\x44\x55\x66\x77\x88') - self.assertEqual(long_to_bytes(0x112233445566778899), b'\x11\x22\x33\x44\x55\x66\x77\x88\x99') - - def test2(self): - self.assertEqual(long_to_bytes(0, 1), b'\x00') - self.assertEqual(long_to_bytes(0, 2), b'\x00\x00') - self.assertEqual(long_to_bytes(1, 3), b'\x00\x00\x01') - self.assertEqual(long_to_bytes(65535, 2), b'\xFF\xFF') - self.assertEqual(long_to_bytes(65536, 2), b'\x00\x01\x00\x00') - self.assertEqual(long_to_bytes(0x100, 1), b'\x01\x00') - self.assertEqual(long_to_bytes(0xFF00000001, 6), b'\x00\xFF\x00\x00\x00\x01') - self.assertEqual(long_to_bytes(0xFF00000001, 8), b'\x00\x00\x00\xFF\x00\x00\x00\x01') - self.assertEqual(long_to_bytes(0xFF00000001, 10), b'\x00\x00\x00\x00\x00\xFF\x00\x00\x00\x01') - self.assertEqual(long_to_bytes(0xFF00000001, 11), b'\x00\x00\x00\x00\x00\x00\xFF\x00\x00\x00\x01') - - def test_err1(self): - self.assertRaises(ValueError, long_to_bytes, -1) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(MiscTests) - tests += list_test_cases(LongTests) - return tests - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/Util/test_rfc1751.py b/resources/lib/deps/Cryptodome/SelfTest/Util/test_rfc1751.py deleted file mode 100644 index 43b137d..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Util/test_rfc1751.py +++ /dev/null @@ -1,38 +0,0 @@ -import unittest - -import binascii -from Cryptodome.Util.RFC1751 import key_to_english, english_to_key - - -class RFC1751_Tests(unittest.TestCase): - - def test1(self): - data = [ - ('EB33F77EE73D4053', 'TIDE ITCH SLOW REIN RULE MOT'), - ('CCAC2AED591056BE4F90FD441C534766', 'RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE'), - ('EFF81F9BFBC65350920CDD7416DE8009', 'TROD MUTE TAIL WARM CHAR KONG HAAG CITY BORE O TEAL AWL') - ] - - for key_hex, words in data: - key_bin = binascii.a2b_hex(key_hex) - - w2 = key_to_english(key_bin) - self.assertEqual(w2, words) - - k2 = english_to_key(words) - self.assertEqual(k2, key_bin) - - def test_error_key_to_english(self): - - self.assertRaises(ValueError, key_to_english, b'0' * 7) - - -def get_tests(config={}): - from Cryptodome.SelfTest.st_common import list_test_cases - tests = list_test_cases(RFC1751_Tests) - return tests - - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/Util/test_strxor.py b/resources/lib/deps/Cryptodome/SelfTest/Util/test_strxor.py deleted file mode 100644 index 6a96129..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/Util/test_strxor.py +++ /dev/null @@ -1,280 +0,0 @@ -# -# SelfTest/Util/test_strxor.py: Self-test for XORing -# -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import unittest -from binascii import unhexlify, hexlify - -from Cryptodome.SelfTest.st_common import list_test_cases -from Cryptodome.Util.strxor import strxor, strxor_c - - -class StrxorTests(unittest.TestCase): - - def test1(self): - term1 = unhexlify(b"ff339a83e5cd4cdf5649") - term2 = unhexlify(b"383d4ba020573314395b") - result = unhexlify(b"c70ed123c59a7fcb6f12") - self.assertEqual(strxor(term1, term2), result) - self.assertEqual(strxor(term2, term1), result) - - def test2(self): - es = b"" - self.assertEqual(strxor(es, es), es) - - def test3(self): - term1 = unhexlify(b"ff339a83e5cd4cdf5649") - all_zeros = b"\x00" * len(term1) - self.assertEqual(strxor(term1, term1), all_zeros) - - def test_wrong_length(self): - term1 = unhexlify(b"ff339a83e5cd4cdf5649") - term2 = unhexlify(b"ff339a83e5cd4cdf564990") - self.assertRaises(ValueError, strxor, term1, term2) - - def test_bytearray(self): - term1 = unhexlify(b"ff339a83e5cd4cdf5649") - term1_ba = bytearray(term1) - term2 = unhexlify(b"383d4ba020573314395b") - result = unhexlify(b"c70ed123c59a7fcb6f12") - - self.assertEqual(strxor(term1_ba, term2), result) - - def test_memoryview(self): - term1 = unhexlify(b"ff339a83e5cd4cdf5649") - term1_mv = memoryview(term1) - term2 = unhexlify(b"383d4ba020573314395b") - result = unhexlify(b"c70ed123c59a7fcb6f12") - - self.assertEqual(strxor(term1_mv, term2), result) - - def test_output_bytearray(self): - """Verify result can be stored in pre-allocated memory""" - - term1 = unhexlify(b"ff339a83e5cd4cdf5649") - term2 = unhexlify(b"383d4ba020573314395b") - original_term1 = term1[:] - original_term2 = term2[:] - expected_xor = unhexlify(b"c70ed123c59a7fcb6f12") - output = bytearray(len(term1)) - - result = strxor(term1, term2, output=output) - - self.assertEqual(result, None) - self.assertEqual(output, expected_xor) - self.assertEqual(term1, original_term1) - self.assertEqual(term2, original_term2) - - def test_output_memoryview(self): - """Verify result can be stored in pre-allocated memory""" - - term1 = unhexlify(b"ff339a83e5cd4cdf5649") - term2 = unhexlify(b"383d4ba020573314395b") - original_term1 = term1[:] - original_term2 = term2[:] - expected_xor = unhexlify(b"c70ed123c59a7fcb6f12") - output = memoryview(bytearray(len(term1))) - - result = strxor(term1, term2, output=output) - - self.assertEqual(result, None) - self.assertEqual(output, expected_xor) - self.assertEqual(term1, original_term1) - self.assertEqual(term2, original_term2) - - def test_output_overlapping_bytearray(self): - """Verify result can be stored in overlapping memory""" - - term1 = bytearray(unhexlify(b"ff339a83e5cd4cdf5649")) - term2 = unhexlify(b"383d4ba020573314395b") - original_term2 = term2[:] - expected_xor = unhexlify(b"c70ed123c59a7fcb6f12") - - result = strxor(term1, term2, output=term1) - - self.assertEqual(result, None) - self.assertEqual(term1, expected_xor) - self.assertEqual(term2, original_term2) - - def test_output_overlapping_memoryview(self): - """Verify result can be stored in overlapping memory""" - - term1 = memoryview(bytearray(unhexlify(b"ff339a83e5cd4cdf5649"))) - term2 = unhexlify(b"383d4ba020573314395b") - original_term2 = term2[:] - expected_xor = unhexlify(b"c70ed123c59a7fcb6f12") - - result = strxor(term1, term2, output=term1) - - self.assertEqual(result, None) - self.assertEqual(term1, expected_xor) - self.assertEqual(term2, original_term2) - - def test_output_ro_bytes(self): - """Verify result cannot be stored in read-only memory""" - - term1 = unhexlify(b"ff339a83e5cd4cdf5649") - term2 = unhexlify(b"383d4ba020573314395b") - - self.assertRaises(TypeError, strxor, term1, term2, output=term1) - - def test_output_ro_memoryview(self): - """Verify result cannot be stored in read-only memory""" - - term1 = memoryview(unhexlify(b"ff339a83e5cd4cdf5649")) - term2 = unhexlify(b"383d4ba020573314395b") - - self.assertRaises(TypeError, strxor, term1, term2, output=term1) - - def test_output_incorrect_length(self): - """Verify result cannot be stored in memory of incorrect length""" - - term1 = unhexlify(b"ff339a83e5cd4cdf5649") - term2 = unhexlify(b"383d4ba020573314395b") - output = bytearray(len(term1) - 1) - - self.assertRaises(ValueError, strxor, term1, term2, output=output) - - -class Strxor_cTests(unittest.TestCase): - - def test1(self): - term1 = unhexlify(b"ff339a83e5cd4cdf5649") - result = unhexlify(b"be72dbc2a48c0d9e1708") - self.assertEqual(strxor_c(term1, 65), result) - - def test2(self): - term1 = unhexlify(b"ff339a83e5cd4cdf5649") - self.assertEqual(strxor_c(term1, 0), term1) - - def test3(self): - self.assertEqual(strxor_c(b"", 90), b"") - - def test_wrong_range(self): - term1 = unhexlify(b"ff339a83e5cd4cdf5649") - self.assertRaises(ValueError, strxor_c, term1, -1) - self.assertRaises(ValueError, strxor_c, term1, 256) - - def test_bytearray(self): - term1 = unhexlify(b"ff339a83e5cd4cdf5649") - term1_ba = bytearray(term1) - result = unhexlify(b"be72dbc2a48c0d9e1708") - - self.assertEqual(strxor_c(term1_ba, 65), result) - - def test_memoryview(self): - term1 = unhexlify(b"ff339a83e5cd4cdf5649") - term1_mv = memoryview(term1) - result = unhexlify(b"be72dbc2a48c0d9e1708") - - self.assertEqual(strxor_c(term1_mv, 65), result) - - def test_output_bytearray(self): - term1 = unhexlify(b"ff339a83e5cd4cdf5649") - original_term1 = term1[:] - expected_result = unhexlify(b"be72dbc2a48c0d9e1708") - output = bytearray(len(term1)) - - result = strxor_c(term1, 65, output=output) - - self.assertEqual(result, None) - self.assertEqual(output, expected_result) - self.assertEqual(term1, original_term1) - - def test_output_memoryview(self): - term1 = unhexlify(b"ff339a83e5cd4cdf5649") - original_term1 = term1[:] - expected_result = unhexlify(b"be72dbc2a48c0d9e1708") - output = memoryview(bytearray(len(term1))) - - result = strxor_c(term1, 65, output=output) - - self.assertEqual(result, None) - self.assertEqual(output, expected_result) - self.assertEqual(term1, original_term1) - - def test_output_overlapping_bytearray(self): - """Verify result can be stored in overlapping memory""" - - term1 = bytearray(unhexlify(b"ff339a83e5cd4cdf5649")) - expected_xor = unhexlify(b"be72dbc2a48c0d9e1708") - - result = strxor_c(term1, 65, output=term1) - - self.assertEqual(result, None) - self.assertEqual(term1, expected_xor) - - def test_output_overlapping_memoryview(self): - """Verify result can be stored in overlapping memory""" - - term1 = memoryview(bytearray(unhexlify(b"ff339a83e5cd4cdf5649"))) - expected_xor = unhexlify(b"be72dbc2a48c0d9e1708") - - result = strxor_c(term1, 65, output=term1) - - self.assertEqual(result, None) - self.assertEqual(term1, expected_xor) - - def test_output_ro_bytes(self): - """Verify result cannot be stored in read-only memory""" - - term1 = unhexlify(b"ff339a83e5cd4cdf5649") - - self.assertRaises(TypeError, strxor_c, term1, 65, output=term1) - - def test_output_ro_memoryview(self): - """Verify result cannot be stored in read-only memory""" - - term1 = memoryview(unhexlify(b"ff339a83e5cd4cdf5649")) - term2 = unhexlify(b"383d4ba020573314395b") - - self.assertRaises(TypeError, strxor_c, term1, 65, output=term1) - - def test_output_incorrect_length(self): - """Verify result cannot be stored in memory of incorrect length""" - - term1 = unhexlify(b"ff339a83e5cd4cdf5649") - output = bytearray(len(term1) - 1) - - self.assertRaises(ValueError, strxor_c, term1, 65, output=output) - - -def get_tests(config={}): - tests = [] - tests += list_test_cases(StrxorTests) - tests += list_test_cases(Strxor_cTests) - return tests - - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') diff --git a/resources/lib/deps/Cryptodome/SelfTest/__init__.py b/resources/lib/deps/Cryptodome/SelfTest/__init__.py deleted file mode 100644 index 09bb48c..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/__init__.py +++ /dev/null @@ -1,102 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/__init__.py: Self-test for PyCrypto -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self tests - -These tests should perform quickly and can ideally be used every time an -application runs. -""" - -import sys -import unittest -from importlib import import_module -from Cryptodome.Util.py3compat import StringIO - - -class SelfTestError(Exception): - def __init__(self, message, result): - Exception.__init__(self, message, result) - self.message = message - self.result = result - - -def run(module=None, verbosity=0, stream=None, tests=None, config=None, **kwargs): - """Execute self-tests. - - This raises SelfTestError if any test is unsuccessful. - - You may optionally pass in a sub-module of SelfTest if you only want to - perform some of the tests. For example, the following would test only the - hash modules: - - Cryptodome.SelfTest.run(Cryptodome.SelfTest.Hash) - - """ - - if config is None: - config = {} - suite = unittest.TestSuite() - if module is None: - if tests is None: - tests = get_tests(config=config) - suite.addTests(tests) - else: - if tests is None: - suite.addTests(module.get_tests(config=config)) - else: - raise ValueError("'module' and 'tests' arguments are mutually exclusive") - if stream is None: - kwargs['stream'] = StringIO() - else: - kwargs['stream'] = stream - runner = unittest.TextTestRunner(verbosity=verbosity, **kwargs) - result = runner.run(suite) - if not result.wasSuccessful(): - if stream is None: - sys.stderr.write(kwargs['stream'].getvalue()) - raise SelfTestError("Self-test failed", result) - return result - - -def get_tests(config={}): - tests = [] - - module_names = [ - "Cipher", "Hash", "Protocol", "PublicKey", "Random", - "Util", "Signature", "IO", "Math", - ] - - for name in module_names: - module = import_module("Cryptodome.SelfTest." + name) - tests += module.get_tests(config=config) - - return tests - - -if __name__ == '__main__': - def suite(): - return unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/SelfTest/__main__.py b/resources/lib/deps/Cryptodome/SelfTest/__main__.py deleted file mode 100644 index 242d781..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/__main__.py +++ /dev/null @@ -1,43 +0,0 @@ -#! /usr/bin/env python -# -# __main__.py : Stand-along loader for PyCryptodome test suite -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -from __future__ import print_function - -import sys - -from Cryptodome import SelfTest - -slow_tests = not ("--skip-slow-tests" in sys.argv) -if not slow_tests: - print("Skipping slow tests") - -wycheproof_warnings = "--wycheproof-warnings" in sys.argv -if wycheproof_warnings: - print("Printing Wycheproof warnings") - -if "-v" in sys.argv: - verbosity=2 -else: - verbosity=1 - -config = {'slow_tests': slow_tests, 'wycheproof_warnings': wycheproof_warnings} -SelfTest.run(stream=sys.stdout, verbosity=verbosity, config=config) diff --git a/resources/lib/deps/Cryptodome/SelfTest/loader.py b/resources/lib/deps/Cryptodome/SelfTest/loader.py deleted file mode 100644 index 1699491..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/loader.py +++ /dev/null @@ -1,239 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2016, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import os -import re -import json -import errno -import binascii -import warnings -from binascii import unhexlify -from Cryptodome.Util.py3compat import FileNotFoundError - - -try: - import pycryptodome_test_vectors # type: ignore - test_vectors_available = True -except ImportError: - test_vectors_available = False - - -def _load_tests(dir_comps, file_in, description, conversions): - """Load and parse a test vector file - - Return a list of objects, one per group of adjacent - KV lines or for a single line in the form "[.*]". - - For a group of lines, the object has one attribute per line. - """ - - line_number = 0 - results = [] - - class TestVector(object): - def __init__(self, description, count): - self.desc = description - self.count = count - self.others = [] - - test_vector = None - count = 0 - new_group = True - - while True: - line_number += 1 - line = file_in.readline() - if not line: - if test_vector is not None: - results.append(test_vector) - break - line = line.strip() - - # Skip comments and empty lines - if line.startswith('#') or not line: - new_group = True - continue - - if line.startswith("["): - if test_vector is not None: - results.append(test_vector) - test_vector = None - results.append(line) - continue - - if new_group: - count += 1 - new_group = False - if test_vector is not None: - results.append(test_vector) - test_vector = TestVector("%s (#%d)" % (description, count), count) - - res = re.match("([A-Za-z0-9]+) = ?(.*)", line) - if not res: - test_vector.others += [line] - else: - token = res.group(1).lower() - data = res.group(2).lower() - - conversion = conversions.get(token, None) - if conversion is None: - if len(data) % 2 != 0: - data = "0" + data - setattr(test_vector, token, binascii.unhexlify(data)) - else: - setattr(test_vector, token, conversion(data)) - - # This line is ignored - return results - - -def load_test_vectors(dir_comps, file_name, description, conversions): - """Load and parse a test vector file, formatted using the NIST style. - - Args: - dir_comps (list of strings): - The path components under the ``pycryptodome_test_vectors`` package. - For instance ``("Cipher", "AES")``. - file_name (string): - The name of the file with the test vectors. - description (string): - A description applicable to the test vectors in the file. - conversions (dictionary): - The dictionary contains functions. - Values in the file that have an entry in this dictionary - will be converted usign the matching function. - Otherwise, values will be considered as hexadecimal and - converted to binary. - - Returns: - A list of test vector objects. - - The file is formatted in the following way: - - - Lines starting with "#" are comments and will be ignored. - - Each test vector is a sequence of 1 or more adjacent lines, where - each lines is an assignement. - - Test vectors are separated by an empty line, a comment, or - a line starting with "[". - - A test vector object has the following attributes: - - - desc (string): description - - counter (int): the order of the test vector in the file (from 1) - - others (list): zero or more lines of the test vector that were not assignments - - left-hand side of each assignment (lowercase): the value of the - assignement, either converted or bytes. - """ - - results = None - - try: - if not test_vectors_available: - raise FileNotFoundError(errno.ENOENT, - os.strerror(errno.ENOENT), - file_name) - - description = "%s test (%s)" % (description, file_name) - - init_dir = os.path.dirname(pycryptodome_test_vectors.__file__) - full_file_name = os.path.join(os.path.join(init_dir, *dir_comps), file_name) - with open(full_file_name) as file_in: - results = _load_tests(dir_comps, file_in, description, conversions) - - except FileNotFoundError: - warnings.warn("Warning: skipping extended tests for " + description, - UserWarning, - stacklevel=2) - - return results - - -def load_test_vectors_wycheproof(dir_comps, file_name, description, - root_tag={}, group_tag={}, unit_tag={}): - - result = [] - try: - if not test_vectors_available: - raise FileNotFoundError(errno.ENOENT, - os.strerror(errno.ENOENT), - file_name) - - init_dir = os.path.dirname(pycryptodome_test_vectors.__file__) - full_file_name = os.path.join(os.path.join(init_dir, *dir_comps), file_name) - with open(full_file_name) as file_in: - tv_tree = json.load(file_in) - - except FileNotFoundError: - warnings.warn("Warning: skipping extended tests for " + description, - UserWarning, - stacklevel=2) - return result - - class TestVector(object): - pass - - common_root = {} - for k, v in root_tag.items(): - common_root[k] = v(tv_tree) - - for group in tv_tree['testGroups']: - - common_group = {} - for k, v in group_tag.items(): - common_group[k] = v(group) - - for test in group['tests']: - tv = TestVector() - - for k, v in common_root.items(): - setattr(tv, k, v) - for k, v in common_group.items(): - setattr(tv, k, v) - - tv.id = test['tcId'] - tv.comment = test['comment'] - for attr in 'key', 'iv', 'aad', 'msg', 'ct', 'tag', 'label', \ - 'ikm', 'salt', 'info', 'okm', 'sig', 'public', 'shared': - if attr in test: - setattr(tv, attr, unhexlify(test[attr])) - tv.filename = file_name - - for k, v in unit_tag.items(): - setattr(tv, k, v(test)) - - tv.valid = test['result'] != "invalid" - tv.warning = test['result'] == "acceptable" - - tv.filename = file_name - - result.append(tv) - - return result - diff --git a/resources/lib/deps/Cryptodome/SelfTest/st_common.py b/resources/lib/deps/Cryptodome/SelfTest/st_common.py deleted file mode 100644 index 3565251..0000000 --- a/resources/lib/deps/Cryptodome/SelfTest/st_common.py +++ /dev/null @@ -1,55 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/st_common.py: Common functions for SelfTest modules -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Common functions for SelfTest modules""" - -import unittest -import binascii -from Cryptodome.Util.py3compat import b - - -def list_test_cases(class_): - """Return a list of TestCase instances given a TestCase class - - This is useful when you have defined test* methods on your TestCase class. - """ - return unittest.TestLoader().loadTestsFromTestCase(class_) - -def strip_whitespace(s): - """Remove whitespace from a text or byte string""" - if isinstance(s,str): - return b("".join(s.split())) - else: - return b("").join(s.split()) - -def a2b_hex(s): - """Convert hexadecimal to binary, ignoring whitespace""" - return binascii.a2b_hex(strip_whitespace(s)) - -def b2a_hex(s): - """Convert binary to hexadecimal""" - # For completeness - return binascii.b2a_hex(s) - -# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/resources/lib/deps/Cryptodome/Signature/DSS.py b/resources/lib/deps/Cryptodome/Signature/DSS.py deleted file mode 100644 index 67f23ac..0000000 --- a/resources/lib/deps/Cryptodome/Signature/DSS.py +++ /dev/null @@ -1,403 +0,0 @@ -# -# Signature/DSS.py : DSS.py -# -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from Cryptodome.Util.asn1 import DerSequence -from Cryptodome.Util.number import long_to_bytes -from Cryptodome.Math.Numbers import Integer - -from Cryptodome.Hash import HMAC -from Cryptodome.PublicKey.ECC import EccKey -from Cryptodome.PublicKey.DSA import DsaKey - -__all__ = ['DssSigScheme', 'new'] - - -class DssSigScheme(object): - """A (EC)DSA signature object. - Do not instantiate directly. - Use :func:`Cryptodome.Signature.DSS.new`. - """ - - def __init__(self, key, encoding, order): - """Create a new Digital Signature Standard (DSS) object. - - Do not instantiate this object directly, - use `Cryptodome.Signature.DSS.new` instead. - """ - - self._key = key - self._encoding = encoding - self._order = order - - self._order_bits = self._order.size_in_bits() - self._order_bytes = (self._order_bits - 1) // 8 + 1 - - def can_sign(self): - """Return ``True`` if this signature object can be used - for signing messages.""" - - return self._key.has_private() - - def _compute_nonce(self, msg_hash): - raise NotImplementedError("To be provided by subclasses") - - def _valid_hash(self, msg_hash): - raise NotImplementedError("To be provided by subclasses") - - def sign(self, msg_hash): - """Compute the DSA/ECDSA signature of a message. - - Args: - msg_hash (hash object): - The hash that was carried out over the message. - The object belongs to the :mod:`Cryptodome.Hash` package. - Under mode ``'fips-186-3'``, the hash must be a FIPS - approved secure hash (SHA-2 or SHA-3). - - :return: The signature as ``bytes`` - :raise ValueError: if the hash algorithm is incompatible to the (EC)DSA key - :raise TypeError: if the (EC)DSA key has no private half - """ - - if not self._key.has_private(): - raise TypeError("Private key is needed to sign") - - if not self._valid_hash(msg_hash): - raise ValueError("Hash is not sufficiently strong") - - # Generate the nonce k (critical!) - nonce = self._compute_nonce(msg_hash) - - # Perform signature using the raw API - z = Integer.from_bytes(msg_hash.digest()[:self._order_bytes]) - sig_pair = self._key._sign(z, nonce) - - # Encode the signature into a single byte string - if self._encoding == 'binary': - output = b"".join([long_to_bytes(x, self._order_bytes) - for x in sig_pair]) - else: - # Dss-sig ::= SEQUENCE { - # r INTEGER, - # s INTEGER - # } - # Ecdsa-Sig-Value ::= SEQUENCE { - # r INTEGER, - # s INTEGER - # } - output = DerSequence(sig_pair).encode() - - return output - - def verify(self, msg_hash, signature): - """Check if a certain (EC)DSA signature is authentic. - - Args: - msg_hash (hash object): - The hash that was carried out over the message. - This is an object belonging to the :mod:`Cryptodome.Hash` module. - Under mode ``'fips-186-3'``, the hash must be a FIPS - approved secure hash (SHA-2 or SHA-3). - - signature (``bytes``): - The signature that needs to be validated. - - :raise ValueError: if the signature is not authentic - """ - - if not self._valid_hash(msg_hash): - raise ValueError("Hash is not sufficiently strong") - - if self._encoding == 'binary': - if len(signature) != (2 * self._order_bytes): - raise ValueError("The signature is not authentic (length)") - r_prime, s_prime = [Integer.from_bytes(x) - for x in (signature[:self._order_bytes], - signature[self._order_bytes:])] - else: - try: - der_seq = DerSequence().decode(signature, strict=True) - except (ValueError, IndexError): - raise ValueError("The signature is not authentic (DER)") - if len(der_seq) != 2 or not der_seq.hasOnlyInts(): - raise ValueError("The signature is not authentic (DER content)") - r_prime, s_prime = Integer(der_seq[0]), Integer(der_seq[1]) - - if not (0 < r_prime < self._order) or not (0 < s_prime < self._order): - raise ValueError("The signature is not authentic (d)") - - z = Integer.from_bytes(msg_hash.digest()[:self._order_bytes]) - result = self._key._verify(z, (r_prime, s_prime)) - if not result: - raise ValueError("The signature is not authentic") - # Make PyCryptodome code to fail - return False - - -class DeterministicDsaSigScheme(DssSigScheme): - # Also applicable to ECDSA - - def __init__(self, key, encoding, order, private_key): - super(DeterministicDsaSigScheme, self).__init__(key, encoding, order) - self._private_key = private_key - - def _bits2int(self, bstr): - """See 2.3.2 in RFC6979""" - - result = Integer.from_bytes(bstr) - q_len = self._order.size_in_bits() - b_len = len(bstr) * 8 - if b_len > q_len: - # Only keep leftmost q_len bits - result >>= (b_len - q_len) - return result - - def _int2octets(self, int_mod_q): - """See 2.3.3 in RFC6979""" - - assert 0 < int_mod_q < self._order - return long_to_bytes(int_mod_q, self._order_bytes) - - def _bits2octets(self, bstr): - """See 2.3.4 in RFC6979""" - - z1 = self._bits2int(bstr) - if z1 < self._order: - z2 = z1 - else: - z2 = z1 - self._order - return self._int2octets(z2) - - def _compute_nonce(self, mhash): - """Generate k in a deterministic way""" - - # See section 3.2 in RFC6979.txt - # Step a - h1 = mhash.digest() - # Step b - mask_v = b'\x01' * mhash.digest_size - # Step c - nonce_k = b'\x00' * mhash.digest_size - - for int_oct in (b'\x00', b'\x01'): - # Step d/f - nonce_k = HMAC.new(nonce_k, - mask_v + int_oct + - self._int2octets(self._private_key) + - self._bits2octets(h1), mhash).digest() - # Step e/g - mask_v = HMAC.new(nonce_k, mask_v, mhash).digest() - - nonce = -1 - while not (0 < nonce < self._order): - # Step h.C (second part) - if nonce != -1: - nonce_k = HMAC.new(nonce_k, mask_v + b'\x00', - mhash).digest() - mask_v = HMAC.new(nonce_k, mask_v, mhash).digest() - - # Step h.A - mask_t = b"" - - # Step h.B - while len(mask_t) < self._order_bytes: - mask_v = HMAC.new(nonce_k, mask_v, mhash).digest() - mask_t += mask_v - - # Step h.C (first part) - nonce = self._bits2int(mask_t) - return nonce - - def _valid_hash(self, msg_hash): - return True - - -class FipsDsaSigScheme(DssSigScheme): - - #: List of L (bit length of p) and N (bit length of q) combinations - #: that are allowed by FIPS 186-3. The security level is provided in - #: Table 2 of FIPS 800-57 (rev3). - _fips_186_3_L_N = ( - (1024, 160), # 80 bits (SHA-1 or stronger) - (2048, 224), # 112 bits (SHA-224 or stronger) - (2048, 256), # 128 bits (SHA-256 or stronger) - (3072, 256) # 256 bits (SHA-512) - ) - - def __init__(self, key, encoding, order, randfunc): - super(FipsDsaSigScheme, self).__init__(key, encoding, order) - self._randfunc = randfunc - - L = Integer(key.p).size_in_bits() - if (L, self._order_bits) not in self._fips_186_3_L_N: - error = ("L/N (%d, %d) is not compliant to FIPS 186-3" - % (L, self._order_bits)) - raise ValueError(error) - - def _compute_nonce(self, msg_hash): - # hash is not used - return Integer.random_range(min_inclusive=1, - max_exclusive=self._order, - randfunc=self._randfunc) - - def _valid_hash(self, msg_hash): - """Verify that SHA-1, SHA-2 or SHA-3 are used""" - return (msg_hash.oid == "1.3.14.3.2.26" or - msg_hash.oid.startswith("2.16.840.1.101.3.4.2.")) - - -class FipsEcDsaSigScheme(DssSigScheme): - - def __init__(self, key, encoding, order, randfunc): - super(FipsEcDsaSigScheme, self).__init__(key, encoding, order) - self._randfunc = randfunc - - def _compute_nonce(self, msg_hash): - return Integer.random_range(min_inclusive=1, - max_exclusive=self._key._curve.order, - randfunc=self._randfunc) - - def _valid_hash(self, msg_hash): - """Verify that the strength of the hash matches or exceeds - the strength of the EC. We fail if the hash is too weak.""" - - modulus_bits = self._key.pointQ.size_in_bits() - - # SHS: SHA-2, SHA-3, truncated SHA-512 - sha224 = ("2.16.840.1.101.3.4.2.4", "2.16.840.1.101.3.4.2.7", "2.16.840.1.101.3.4.2.5") - sha256 = ("2.16.840.1.101.3.4.2.1", "2.16.840.1.101.3.4.2.8", "2.16.840.1.101.3.4.2.6") - sha384 = ("2.16.840.1.101.3.4.2.2", "2.16.840.1.101.3.4.2.9") - sha512 = ("2.16.840.1.101.3.4.2.3", "2.16.840.1.101.3.4.2.10") - shs = sha224 + sha256 + sha384 + sha512 - - try: - result = msg_hash.oid in shs - except AttributeError: - result = False - return result - - -def new(key, mode, encoding='binary', randfunc=None): - """Create a signature object :class:`DssSigScheme` that - can perform (EC)DSA signature or verification. - - .. note:: - Refer to `NIST SP 800 Part 1 Rev 4`_ (or newer release) for an - overview of the recommended key lengths. - - Args: - key (:class:`Cryptodome.PublicKey.DSA` or :class:`Cryptodome.PublicKey.ECC`): - The key to use for computing the signature (*private* keys only) - or for verifying one. - For DSA keys, let ``L`` and ``N`` be the bit lengths of the modulus ``p`` - and of ``q``: the pair ``(L,N)`` must appear in the following list, - in compliance to section 4.2 of `FIPS 186-4`_: - - - (1024, 160) *legacy only; do not create new signatures with this* - - (2048, 224) *deprecated; do not create new signatures with this* - - (2048, 256) - - (3072, 256) - - For ECC, only keys over P-224, P-256, P-384, and P-521 are accepted. - - mode (string): - The parameter can take these values: - - - ``'fips-186-3'``. The signature generation is randomized and carried out - according to `FIPS 186-3`_: the nonce ``k`` is taken from the RNG. - - ``'deterministic-rfc6979'``. The signature generation is not - randomized. See RFC6979_. - - encoding (string): - How the signature is encoded. This value determines the output of - :meth:`sign` and the input to :meth:`verify`. - - The following values are accepted: - - - ``'binary'`` (default), the signature is the raw concatenation - of ``r`` and ``s``. It is defined in the IEEE P.1363 standard. - For DSA, the size in bytes of the signature is ``N/4`` bytes - (e.g. 64 for ``N=256``). - For ECDSA, the signature is always twice the length of a point - coordinate (e.g. 64 bytes for P-256). - - - ``'der'``, the signature is a ASN.1 DER SEQUENCE - with two INTEGERs (``r`` and ``s``). It is defined in RFC3279_. - The size of the signature is variable. - - randfunc (callable): - A function that returns random ``bytes``, of a given length. - If omitted, the internal RNG is used. - Only applicable for the *'fips-186-3'* mode. - - .. _FIPS 186-3: http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf - .. _FIPS 186-4: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf - .. _NIST SP 800 Part 1 Rev 4: http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57pt1r4.pdf - .. _RFC6979: http://tools.ietf.org/html/rfc6979 - .. _RFC3279: https://tools.ietf.org/html/rfc3279#section-2.2.2 - """ - - # The goal of the 'mode' parameter is to avoid to - # have the current version of the standard as default. - # - # Over time, such version will be superseded by (for instance) - # FIPS 186-4 and it will be odd to have -3 as default. - - if encoding not in ('binary', 'der'): - raise ValueError("Unknown encoding '%s'" % encoding) - - if isinstance(key, EccKey): - order = key._curve.order - private_key_attr = 'd' - if key._curve.name == "ed25519": - raise ValueError("ECC key is not on a NIST P curve") - elif isinstance(key, DsaKey): - order = Integer(key.q) - private_key_attr = 'x' - else: - raise ValueError("Unsupported key type " + str(type(key))) - - if key.has_private(): - private_key = getattr(key, private_key_attr) - else: - private_key = None - - if mode == 'deterministic-rfc6979': - return DeterministicDsaSigScheme(key, encoding, order, private_key) - elif mode == 'fips-186-3': - if isinstance(key, EccKey): - return FipsEcDsaSigScheme(key, encoding, order, randfunc) - else: - return FipsDsaSigScheme(key, encoding, order, randfunc) - else: - raise ValueError("Unknown DSS mode '%s'" % mode) diff --git a/resources/lib/deps/Cryptodome/Signature/DSS.pyi b/resources/lib/deps/Cryptodome/Signature/DSS.pyi deleted file mode 100644 index 52ecc8f..0000000 --- a/resources/lib/deps/Cryptodome/Signature/DSS.pyi +++ /dev/null @@ -1,27 +0,0 @@ -from typing import Union, Optional, Callable -from typing_extensions import Protocol - -from Cryptodome.PublicKey.DSA import DsaKey -from Cryptodome.PublicKey.ECC import EccKey - -class Hash(Protocol): - def digest(self) -> bytes: ... - -__all__ = ['new'] - -class DssSigScheme: - def __init__(self, key: Union[DsaKey, EccKey], encoding: str, order: int) -> None: ... - def can_sign(self) -> bool: ... - def sign(self, msg_hash: Hash) -> bytes: ... - def verify(self, msg_hash: Hash, signature: bytes) -> bool: ... - -class DeterministicDsaSigScheme(DssSigScheme): - def __init__(self, key, encoding, order, private_key) -> None: ... - -class FipsDsaSigScheme(DssSigScheme): - def __init__(self, key: DsaKey, encoding: str, order: int, randfunc: Callable) -> None: ... - -class FipsEcDsaSigScheme(DssSigScheme): - def __init__(self, key: EccKey, encoding: str, order: int, randfunc: Callable) -> None: ... - -def new(key: Union[DsaKey, EccKey], mode: str, encoding: Optional[str]='binary', randfunc: Optional[Callable]=None) -> Union[DeterministicDsaSigScheme, FipsDsaSigScheme, FipsEcDsaSigScheme]: ... diff --git a/resources/lib/deps/Cryptodome/Signature/PKCS1_PSS.py b/resources/lib/deps/Cryptodome/Signature/PKCS1_PSS.py deleted file mode 100644 index 1e7e5b5..0000000 --- a/resources/lib/deps/Cryptodome/Signature/PKCS1_PSS.py +++ /dev/null @@ -1,55 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -""" -Legacy module for PKCS#1 PSS signatures. - -:undocumented: __package__ -""" - -import types - -from Cryptodome.Signature import pss - - -def _pycrypto_verify(self, hash_object, signature): - try: - self._verify(hash_object, signature) - except (ValueError, TypeError): - return False - return True - - -def new(rsa_key, mgfunc=None, saltLen=None, randfunc=None): - pkcs1 = pss.new(rsa_key, mask_func=mgfunc, - salt_bytes=saltLen, rand_func=randfunc) - pkcs1._verify = pkcs1.verify - pkcs1.verify = types.MethodType(_pycrypto_verify, pkcs1) - return pkcs1 diff --git a/resources/lib/deps/Cryptodome/Signature/PKCS1_PSS.pyi b/resources/lib/deps/Cryptodome/Signature/PKCS1_PSS.pyi deleted file mode 100644 index e7424f5..0000000 --- a/resources/lib/deps/Cryptodome/Signature/PKCS1_PSS.pyi +++ /dev/null @@ -1,28 +0,0 @@ -from typing import Union, Callable, Optional -from typing_extensions import Protocol - -from Cryptodome.PublicKey.RSA import RsaKey - - -class Hash(Protocol): - def digest(self) -> bytes: ... - def update(self, bytes) -> None: ... - - -class HashModule(Protocol): - @staticmethod - def new(data: Optional[bytes]) -> Hash: ... - - -MaskFunction = Callable[[bytes, int, Union[Hash, HashModule]], bytes] -RndFunction = Callable[[int], bytes] - -class PSS_SigScheme: - def __init__(self, key: RsaKey, mgfunc: MaskFunction, saltLen: int, randfunc: RndFunction) -> None: ... - def can_sign(self) -> bool: ... - def sign(self, msg_hash: Hash) -> bytes: ... - def verify(self, msg_hash: Hash, signature: bytes) -> bool: ... - - - -def new(rsa_key: RsaKey, mgfunc: Optional[MaskFunction]=None, saltLen: Optional[int]=None, randfunc: Optional[RndFunction]=None) -> PSS_SigScheme: ... diff --git a/resources/lib/deps/Cryptodome/Signature/PKCS1_v1_5.py b/resources/lib/deps/Cryptodome/Signature/PKCS1_v1_5.py deleted file mode 100644 index d560663..0000000 --- a/resources/lib/deps/Cryptodome/Signature/PKCS1_v1_5.py +++ /dev/null @@ -1,53 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -""" -Legacy module for PKCS#1 v1.5 signatures. - -:undocumented: __package__ -""" - -import types - -from Cryptodome.Signature import pkcs1_15 - -def _pycrypto_verify(self, hash_object, signature): - try: - self._verify(hash_object, signature) - except (ValueError, TypeError): - return False - return True - -def new(rsa_key): - pkcs1 = pkcs1_15.new(rsa_key) - pkcs1._verify = pkcs1.verify - pkcs1.verify = types.MethodType(_pycrypto_verify, pkcs1) - return pkcs1 - diff --git a/resources/lib/deps/Cryptodome/Signature/PKCS1_v1_5.pyi b/resources/lib/deps/Cryptodome/Signature/PKCS1_v1_5.pyi deleted file mode 100644 index d02555c..0000000 --- a/resources/lib/deps/Cryptodome/Signature/PKCS1_v1_5.pyi +++ /dev/null @@ -1,16 +0,0 @@ -from typing import Optional -from typing_extensions import Protocol - -from Cryptodome.PublicKey.RSA import RsaKey - -class Hash(Protocol): - def digest(self) -> bytes: ... - -class PKCS115_SigScheme: - def __init__(self, rsa_key: RsaKey) -> None: ... - def can_sign(self) -> bool: ... - def sign(self, msg_hash: Hash) -> bytes: ... - def verify(self, msg_hash: Hash, signature: bytes) -> bool: ... - - -def new(rsa_key: RsaKey) -> PKCS115_SigScheme: ... diff --git a/resources/lib/deps/Cryptodome/Signature/__init__.py b/resources/lib/deps/Cryptodome/Signature/__init__.py deleted file mode 100644 index 11ca64c..0000000 --- a/resources/lib/deps/Cryptodome/Signature/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -"""Digital signature protocols - -A collection of standardized protocols to carry out digital signatures. -""" - -__all__ = ['PKCS1_v1_5', 'PKCS1_PSS', 'DSS', 'pkcs1_15', 'pss', 'eddsa'] diff --git a/resources/lib/deps/Cryptodome/Signature/eddsa.py b/resources/lib/deps/Cryptodome/Signature/eddsa.py deleted file mode 100644 index 638b96b..0000000 --- a/resources/lib/deps/Cryptodome/Signature/eddsa.py +++ /dev/null @@ -1,343 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2022, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from Cryptodome.Math.Numbers import Integer - -from Cryptodome.Hash import SHA512, SHAKE256 -from Cryptodome.Util.py3compat import bchr, is_bytes -from Cryptodome.PublicKey.ECC import (EccKey, - construct, - _import_ed25519_public_key, - _import_ed448_public_key) - - -def import_public_key(encoded): - """Create a new Ed25519 or Ed448 public key object, - starting from the key encoded as raw ``bytes``, - in the format described in RFC8032. - - Args: - encoded (bytes): - The EdDSA public key to import. - It must be 32 bytes for Ed25519, and 57 bytes for Ed448. - - Returns: - :class:`Cryptodome.PublicKey.EccKey` : a new ECC key object. - - Raises: - ValueError: when the given key cannot be parsed. - """ - - if len(encoded) == 32: - x, y = _import_ed25519_public_key(encoded) - curve_name = "Ed25519" - elif len(encoded) == 57: - x, y = _import_ed448_public_key(encoded) - curve_name = "Ed448" - else: - raise ValueError("Not an EdDSA key (%d bytes)" % len(encoded)) - return construct(curve=curve_name, point_x=x, point_y=y) - - -def import_private_key(encoded): - """Create a new Ed25519 or Ed448 private key object, - starting from the key encoded as raw ``bytes``, - in the format described in RFC8032. - - Args: - encoded (bytes): - The EdDSA private key to import. - It must be 32 bytes for Ed25519, and 57 bytes for Ed448. - - Returns: - :class:`Cryptodome.PublicKey.EccKey` : a new ECC key object. - - Raises: - ValueError: when the given key cannot be parsed. - """ - - if len(encoded) == 32: - curve_name = "ed25519" - elif len(encoded) == 57: - curve_name = "ed448" - else: - raise ValueError("Incorrect length. Only EdDSA private keys are supported.") - - # Note that the private key is truly a sequence of random bytes, - # so we cannot check its correctness in any way. - - return construct(seed=encoded, curve=curve_name) - - -class EdDSASigScheme(object): - """An EdDSA signature object. - Do not instantiate directly. - Use :func:`Cryptodome.Signature.eddsa.new`. - """ - - def __init__(self, key, context): - """Create a new EdDSA object. - - Do not instantiate this object directly, - use `Cryptodome.Signature.DSS.new` instead. - """ - - self._key = key - self._context = context - self._A = key._export_eddsa() - self._order = key._curve.order - - def can_sign(self): - """Return ``True`` if this signature object can be used - for signing messages.""" - - return self._key.has_private() - - def sign(self, msg_or_hash): - """Compute the EdDSA signature of a message. - - Args: - msg_or_hash (bytes or a hash object): - The message to sign (``bytes``, in case of *PureEdDSA*) or - the hash that was carried out over the message (hash object, for *HashEdDSA*). - - The hash object must be :class:`Cryptodome.Hash.SHA512` for Ed25519, - and :class:`Cryptodome.Hash.SHAKE256` object for Ed448. - - :return: The signature as ``bytes``. It is always 64 bytes for Ed25519, and 114 bytes for Ed448. - :raise TypeError: if the EdDSA key has no private half - """ - - if not self._key.has_private(): - raise TypeError("Private key is needed to sign") - - if self._key._curve.name == "ed25519": - ph = isinstance(msg_or_hash, SHA512.SHA512Hash) - if not (ph or is_bytes(msg_or_hash)): - raise TypeError("'msg_or_hash' must be bytes of a SHA-512 hash") - eddsa_sign_method = self._sign_ed25519 - - elif self._key._curve.name == "ed448": - ph = isinstance(msg_or_hash, SHAKE256.SHAKE256_XOF) - if not (ph or is_bytes(msg_or_hash)): - raise TypeError("'msg_or_hash' must be bytes of a SHAKE256 hash") - eddsa_sign_method = self._sign_ed448 - - else: - raise ValueError("Incorrect curve for EdDSA") - - return eddsa_sign_method(msg_or_hash, ph) - - def _sign_ed25519(self, msg_or_hash, ph): - - if self._context or ph: - flag = int(ph) - # dom2(flag, self._context) - dom2 = b'SigEd25519 no Ed25519 collisions' + bchr(flag) + \ - bchr(len(self._context)) + self._context - else: - dom2 = b'' - - PHM = msg_or_hash.digest() if ph else msg_or_hash - - # See RFC 8032, section 5.1.6 - - # Step 2 - r_hash = SHA512.new(dom2 + self._key._prefix + PHM).digest() - r = Integer.from_bytes(r_hash, 'little') % self._order - # Step 3 - R_pk = EccKey(point=r * self._key._curve.G)._export_eddsa() - # Step 4 - k_hash = SHA512.new(dom2 + R_pk + self._A + PHM).digest() - k = Integer.from_bytes(k_hash, 'little') % self._order - # Step 5 - s = (r + k * self._key.d) % self._order - - return R_pk + s.to_bytes(32, 'little') - - def _sign_ed448(self, msg_or_hash, ph): - - flag = int(ph) - # dom4(flag, self._context) - dom4 = b'SigEd448' + bchr(flag) + \ - bchr(len(self._context)) + self._context - - PHM = msg_or_hash.read(64) if ph else msg_or_hash - - # See RFC 8032, section 5.2.6 - - # Step 2 - r_hash = SHAKE256.new(dom4 + self._key._prefix + PHM).read(114) - r = Integer.from_bytes(r_hash, 'little') % self._order - # Step 3 - R_pk = EccKey(point=r * self._key._curve.G)._export_eddsa() - # Step 4 - k_hash = SHAKE256.new(dom4 + R_pk + self._A + PHM).read(114) - k = Integer.from_bytes(k_hash, 'little') % self._order - # Step 5 - s = (r + k * self._key.d) % self._order - - return R_pk + s.to_bytes(57, 'little') - - def verify(self, msg_or_hash, signature): - """Check if an EdDSA signature is authentic. - - Args: - msg_or_hash (bytes or a hash object): - The message to verify (``bytes``, in case of *PureEdDSA*) or - the hash that was carried out over the message (hash object, for *HashEdDSA*). - - The hash object must be :class:`Cryptodome.Hash.SHA512` object for Ed25519, - and :class:`Cryptodome.Hash.SHAKE256` for Ed448. - - signature (``bytes``): - The signature that needs to be validated. - It must be 64 bytes for Ed25519, and 114 bytes for Ed448. - - :raise ValueError: if the signature is not authentic - """ - - if self._key._curve.name == "ed25519": - ph = isinstance(msg_or_hash, SHA512.SHA512Hash) - if not (ph or is_bytes(msg_or_hash)): - raise TypeError("'msg_or_hash' must be bytes of a SHA-512 hash") - eddsa_verify_method = self._verify_ed25519 - - elif self._key._curve.name == "ed448": - ph = isinstance(msg_or_hash, SHAKE256.SHAKE256_XOF) - if not (ph or is_bytes(msg_or_hash)): - raise TypeError("'msg_or_hash' must be bytes of a SHAKE256 hash") - eddsa_verify_method = self._verify_ed448 - - else: - raise ValueError("Incorrect curve for EdDSA") - - return eddsa_verify_method(msg_or_hash, signature, ph) - - def _verify_ed25519(self, msg_or_hash, signature, ph): - - if len(signature) != 64: - raise ValueError("The signature is not authentic (length)") - - if self._context or ph: - flag = int(ph) - dom2 = b'SigEd25519 no Ed25519 collisions' + bchr(flag) + \ - bchr(len(self._context)) + self._context - else: - dom2 = b'' - - PHM = msg_or_hash.digest() if ph else msg_or_hash - - # Section 5.1.7 - - # Step 1 - try: - R = import_public_key(signature[:32]).pointQ - except ValueError: - raise ValueError("The signature is not authentic (R)") - s = Integer.from_bytes(signature[32:], 'little') - if s > self._order: - raise ValueError("The signature is not authentic (S)") - # Step 2 - k_hash = SHA512.new(dom2 + signature[:32] + self._A + PHM).digest() - k = Integer.from_bytes(k_hash, 'little') % self._order - # Step 3 - point1 = s * 8 * self._key._curve.G - # OPTIMIZE: with double-scalar multiplication, with no SCA - # countermeasures because it is public values - point2 = 8 * R + k * 8 * self._key.pointQ - if point1 != point2: - raise ValueError("The signature is not authentic") - - def _verify_ed448(self, msg_or_hash, signature, ph): - - if len(signature) != 114: - raise ValueError("The signature is not authentic (length)") - - flag = int(ph) - # dom4(flag, self._context) - dom4 = b'SigEd448' + bchr(flag) + \ - bchr(len(self._context)) + self._context - - PHM = msg_or_hash.read(64) if ph else msg_or_hash - - # Section 5.2.7 - - # Step 1 - try: - R = import_public_key(signature[:57]).pointQ - except ValueError: - raise ValueError("The signature is not authentic (R)") - s = Integer.from_bytes(signature[57:], 'little') - if s > self._order: - raise ValueError("The signature is not authentic (S)") - # Step 2 - k_hash = SHAKE256.new(dom4 + signature[:57] + self._A + PHM).read(114) - k = Integer.from_bytes(k_hash, 'little') % self._order - # Step 3 - point1 = s * 8 * self._key._curve.G - # OPTIMIZE: with double-scalar multiplication, with no SCA - # countermeasures because it is public values - point2 = 8 * R + k * 8 * self._key.pointQ - if point1 != point2: - raise ValueError("The signature is not authentic") - - -def new(key, mode, context=None): - """Create a signature object :class:`EdDSASigScheme` that - can perform or verify an EdDSA signature. - - Args: - key (:class:`Cryptodome.PublicKey.ECC` object): - The key to use for computing the signature (*private* keys only) - or for verifying one. - The key must be on the curve ``Ed25519`` or ``Ed448``. - - mode (string): - This parameter must be ``'rfc8032'``. - - context (bytes): - Up to 255 bytes of `context `_, - which is a constant byte string to segregate different protocols or - different applications of the same key. - """ - - if not isinstance(key, EccKey) or not key._is_eddsa(): - raise ValueError("EdDSA can only be used with EdDSA keys") - - if mode != 'rfc8032': - raise ValueError("Mode must be 'rfc8032'") - - if context is None: - context = b'' - elif len(context) > 255: - raise ValueError("Context for EdDSA must not be longer than 255 bytes") - - return EdDSASigScheme(key, context) diff --git a/resources/lib/deps/Cryptodome/Signature/eddsa.pyi b/resources/lib/deps/Cryptodome/Signature/eddsa.pyi deleted file mode 100644 index 809a7ad..0000000 --- a/resources/lib/deps/Cryptodome/Signature/eddsa.pyi +++ /dev/null @@ -1,21 +0,0 @@ -from typing import Union, Optional -from typing_extensions import Protocol -from Cryptodome.PublicKey.ECC import EccKey - -class Hash(Protocol): - def digest(self) -> bytes: ... - -class XOF(Protocol): - def read(self, len: int) -> bytes: ... - -def import_public_key(encoded: bytes) -> EccKey: ... -def import_private_key(encoded: bytes) -> EccKey: ... - -class EdDSASigScheme(object): - - def __init__(self, key: EccKey, context: bytes) -> None: ... - def can_sign(self) -> bool: ... - def sign(self, msg_or_hash: Union[bytes, Hash, XOF]) -> bytes: ... - def verify(self, msg_or_hash: Union[bytes, Hash, XOF], signature: bytes) -> None: ... - -def new(key: EccKey, mode: str, context: Optional[bytes]=None) -> EdDSASigScheme: ... diff --git a/resources/lib/deps/Cryptodome/Signature/pkcs1_15.py b/resources/lib/deps/Cryptodome/Signature/pkcs1_15.py deleted file mode 100644 index bdde78a..0000000 --- a/resources/lib/deps/Cryptodome/Signature/pkcs1_15.py +++ /dev/null @@ -1,223 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import Cryptodome.Util.number -from Cryptodome.Util.number import ceil_div, bytes_to_long, long_to_bytes -from Cryptodome.Util.asn1 import DerSequence, DerNull, DerOctetString, DerObjectId - -class PKCS115_SigScheme: - """A signature object for ``RSASSA-PKCS1-v1_5``. - Do not instantiate directly. - Use :func:`Cryptodome.Signature.pkcs1_15.new`. - """ - - def __init__(self, rsa_key): - """Initialize this PKCS#1 v1.5 signature scheme object. - - :Parameters: - rsa_key : an RSA key object - Creation of signatures is only possible if this is a *private* - RSA key. Verification of signatures is always possible. - """ - self._key = rsa_key - - def can_sign(self): - """Return ``True`` if this object can be used to sign messages.""" - return self._key.has_private() - - def sign(self, msg_hash): - """Create the PKCS#1 v1.5 signature of a message. - - This function is also called ``RSASSA-PKCS1-V1_5-SIGN`` and - it is specified in - `section 8.2.1 of RFC8017 `_. - - :parameter msg_hash: - This is an object from the :mod:`Cryptodome.Hash` package. - It has been used to digest the message to sign. - :type msg_hash: hash object - - :return: the signature encoded as a *byte string*. - :raise ValueError: if the RSA key is not long enough for the given hash algorithm. - :raise TypeError: if the RSA key has no private half. - """ - - # See 8.2.1 in RFC3447 - modBits = Cryptodome.Util.number.size(self._key.n) - k = ceil_div(modBits,8) # Convert from bits to bytes - - # Step 1 - em = _EMSA_PKCS1_V1_5_ENCODE(msg_hash, k) - # Step 2a (OS2IP) - em_int = bytes_to_long(em) - # Step 2b (RSASP1) and Step 2c (I2OSP) - signature = self._key._decrypt_to_bytes(em_int) - # Verify no faults occurred - if em_int != pow(bytes_to_long(signature), self._key.e, self._key.n): - raise ValueError("Fault detected in RSA private key operation") - return signature - - def verify(self, msg_hash, signature): - """Check if the PKCS#1 v1.5 signature over a message is valid. - - This function is also called ``RSASSA-PKCS1-V1_5-VERIFY`` and - it is specified in - `section 8.2.2 of RFC8037 `_. - - :parameter msg_hash: - The hash that was carried out over the message. This is an object - belonging to the :mod:`Cryptodome.Hash` module. - :type parameter: hash object - - :parameter signature: - The signature that needs to be validated. - :type signature: byte string - - :raise ValueError: if the signature is not valid. - """ - - # See 8.2.2 in RFC3447 - modBits = Cryptodome.Util.number.size(self._key.n) - k = ceil_div(modBits, 8) # Convert from bits to bytes - - # Step 1 - if len(signature) != k: - raise ValueError("Invalid signature") - # Step 2a (O2SIP) - signature_int = bytes_to_long(signature) - # Step 2b (RSAVP1) - em_int = self._key._encrypt(signature_int) - # Step 2c (I2OSP) - em1 = long_to_bytes(em_int, k) - # Step 3 - try: - possible_em1 = [ _EMSA_PKCS1_V1_5_ENCODE(msg_hash, k, True) ] - # MD2/4/5 hashes always require NULL params in AlgorithmIdentifier. - # For all others, it is optional. - try: - algorithm_is_md = msg_hash.oid.startswith('1.2.840.113549.2.') - except AttributeError: - algorithm_is_md = False - if not algorithm_is_md: # MD2/MD4/MD5 - possible_em1.append(_EMSA_PKCS1_V1_5_ENCODE(msg_hash, k, False)) - except ValueError: - raise ValueError("Invalid signature") - # Step 4 - # By comparing the full encodings (as opposed to checking each - # of its components one at a time) we avoid attacks to the padding - # scheme like Bleichenbacher's (see http://www.mail-archive.com/cryptography@metzdowd.com/msg06537). - # - if em1 not in possible_em1: - raise ValueError("Invalid signature") - pass - - -def _EMSA_PKCS1_V1_5_ENCODE(msg_hash, emLen, with_hash_parameters=True): - """ - Implement the ``EMSA-PKCS1-V1_5-ENCODE`` function, as defined - in PKCS#1 v2.1 (RFC3447, 9.2). - - ``_EMSA-PKCS1-V1_5-ENCODE`` actually accepts the message ``M`` as input, - and hash it internally. Here, we expect that the message has already - been hashed instead. - - :Parameters: - msg_hash : hash object - The hash object that holds the digest of the message being signed. - emLen : int - The length the final encoding must have, in bytes. - with_hash_parameters : bool - If True (default), include NULL parameters for the hash - algorithm in the ``digestAlgorithm`` SEQUENCE. - - :attention: the early standard (RFC2313) stated that ``DigestInfo`` - had to be BER-encoded. This means that old signatures - might have length tags in indefinite form, which - is not supported in DER. Such encoding cannot be - reproduced by this function. - - :Return: An ``emLen`` byte long string that encodes the hash. - """ - - # First, build the ASN.1 DER object DigestInfo: - # - # DigestInfo ::= SEQUENCE { - # digestAlgorithm AlgorithmIdentifier, - # digest OCTET STRING - # } - # - # where digestAlgorithm identifies the hash function and shall be an - # algorithm ID with an OID in the set PKCS1-v1-5DigestAlgorithms. - # - # PKCS1-v1-5DigestAlgorithms ALGORITHM-IDENTIFIER ::= { - # { OID id-md2 PARAMETERS NULL }| - # { OID id-md5 PARAMETERS NULL }| - # { OID id-sha1 PARAMETERS NULL }| - # { OID id-sha256 PARAMETERS NULL }| - # { OID id-sha384 PARAMETERS NULL }| - # { OID id-sha512 PARAMETERS NULL } - # } - # - # Appendix B.1 also says that for SHA-1/-2 algorithms, the parameters - # should be omitted. They may be present, but when they are, they shall - # have NULL value. - - digestAlgo = DerSequence([ DerObjectId(msg_hash.oid).encode() ]) - - if with_hash_parameters: - digestAlgo.append(DerNull().encode()) - - digest = DerOctetString(msg_hash.digest()) - digestInfo = DerSequence([ - digestAlgo.encode(), - digest.encode() - ]).encode() - - # We need at least 11 bytes for the remaining data: 3 fixed bytes and - # at least 8 bytes of padding). - if emLen bytes: ... - -class PKCS115_SigScheme: - def __init__(self, rsa_key: RsaKey) -> None: ... - def can_sign(self) -> bool: ... - def sign(self, msg_hash: Hash) -> bytes: ... - def verify(self, msg_hash: Hash, signature: bytes) -> None: ... - -def _EMSA_PKCS1_V1_5_ENCODE(msg_hash: Hash, emLen: int, with_hash_parameters: Optional[bool]=True) -> bytes: ... - -def new(rsa_key: RsaKey) -> PKCS115_SigScheme: ... diff --git a/resources/lib/deps/Cryptodome/Signature/pss.py b/resources/lib/deps/Cryptodome/Signature/pss.py deleted file mode 100644 index b929e26..0000000 --- a/resources/lib/deps/Cryptodome/Signature/pss.py +++ /dev/null @@ -1,387 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from Cryptodome.Util.py3compat import bchr, bord, iter_range -import Cryptodome.Util.number -from Cryptodome.Util.number import (ceil_div, - long_to_bytes, - bytes_to_long - ) -from Cryptodome.Util.strxor import strxor -from Cryptodome import Random - - -class PSS_SigScheme: - """A signature object for ``RSASSA-PSS``. - Do not instantiate directly. - Use :func:`Cryptodome.Signature.pss.new`. - """ - - def __init__(self, key, mgfunc, saltLen, randfunc): - """Initialize this PKCS#1 PSS signature scheme object. - - :Parameters: - key : an RSA key object - If a private half is given, both signature and - verification are possible. - If a public half is given, only verification is possible. - mgfunc : callable - A mask generation function that accepts two parameters: - a string to use as seed, and the lenth of the mask to - generate, in bytes. - saltLen : integer - Length of the salt, in bytes. - randfunc : callable - A function that returns random bytes. - """ - - self._key = key - self._saltLen = saltLen - self._mgfunc = mgfunc - self._randfunc = randfunc - - def can_sign(self): - """Return ``True`` if this object can be used to sign messages.""" - return self._key.has_private() - - def sign(self, msg_hash): - """Create the PKCS#1 PSS signature of a message. - - This function is also called ``RSASSA-PSS-SIGN`` and - it is specified in - `section 8.1.1 of RFC8017 `_. - - :parameter msg_hash: - This is an object from the :mod:`Cryptodome.Hash` package. - It has been used to digest the message to sign. - :type msg_hash: hash object - - :return: the signature encoded as a *byte string*. - :raise ValueError: if the RSA key is not long enough for the given hash algorithm. - :raise TypeError: if the RSA key has no private half. - """ - - # Set defaults for salt length and mask generation function - if self._saltLen is None: - sLen = msg_hash.digest_size - else: - sLen = self._saltLen - - if self._mgfunc is None: - mgf = lambda x, y: MGF1(x, y, msg_hash) - else: - mgf = self._mgfunc - - modBits = Cryptodome.Util.number.size(self._key.n) - - # See 8.1.1 in RFC3447 - k = ceil_div(modBits, 8) # k is length in bytes of the modulus - # Step 1 - em = _EMSA_PSS_ENCODE(msg_hash, modBits-1, self._randfunc, mgf, sLen) - # Step 2a (OS2IP) - em_int = bytes_to_long(em) - # Step 2b (RSASP1) and Step 2c (I2OSP) - signature = self._key._decrypt_to_bytes(em_int) - # Verify no faults occurred - if em_int != pow(bytes_to_long(signature), self._key.e, self._key.n): - raise ValueError("Fault detected in RSA private key operation") - return signature - - def verify(self, msg_hash, signature): - """Check if the PKCS#1 PSS signature over a message is valid. - - This function is also called ``RSASSA-PSS-VERIFY`` and - it is specified in - `section 8.1.2 of RFC8037 `_. - - :parameter msg_hash: - The hash that was carried out over the message. This is an object - belonging to the :mod:`Cryptodome.Hash` module. - :type parameter: hash object - - :parameter signature: - The signature that needs to be validated. - :type signature: bytes - - :raise ValueError: if the signature is not valid. - """ - - # Set defaults for salt length and mask generation function - if self._saltLen is None: - sLen = msg_hash.digest_size - else: - sLen = self._saltLen - if self._mgfunc: - mgf = self._mgfunc - else: - mgf = lambda x, y: MGF1(x, y, msg_hash) - - modBits = Cryptodome.Util.number.size(self._key.n) - - # See 8.1.2 in RFC3447 - k = ceil_div(modBits, 8) # Convert from bits to bytes - # Step 1 - if len(signature) != k: - raise ValueError("Incorrect signature") - # Step 2a (O2SIP) - signature_int = bytes_to_long(signature) - # Step 2b (RSAVP1) - em_int = self._key._encrypt(signature_int) - # Step 2c (I2OSP) - emLen = ceil_div(modBits - 1, 8) - em = long_to_bytes(em_int, emLen) - # Step 3/4 - _EMSA_PSS_VERIFY(msg_hash, em, modBits-1, mgf, sLen) - - -def MGF1(mgfSeed, maskLen, hash_gen): - """Mask Generation Function, described in `B.2.1 of RFC8017 - `_. - - :param mfgSeed: - seed from which the mask is generated - :type mfgSeed: byte string - - :param maskLen: - intended length in bytes of the mask - :type maskLen: integer - - :param hash_gen: - A module or a hash object from :mod:`Cryptodome.Hash` - :type hash_object: - - :return: the mask, as a *byte string* - """ - - T = b"" - for counter in iter_range(ceil_div(maskLen, hash_gen.digest_size)): - c = long_to_bytes(counter, 4) - hobj = hash_gen.new() - hobj.update(mgfSeed + c) - T = T + hobj.digest() - assert(len(T) >= maskLen) - return T[:maskLen] - - -def _EMSA_PSS_ENCODE(mhash, emBits, randFunc, mgf, sLen): - r""" - Implement the ``EMSA-PSS-ENCODE`` function, as defined - in PKCS#1 v2.1 (RFC3447, 9.1.1). - - The original ``EMSA-PSS-ENCODE`` actually accepts the message ``M`` - as input, and hash it internally. Here, we expect that the message - has already been hashed instead. - - :Parameters: - mhash : hash object - The hash object that holds the digest of the message being signed. - emBits : int - Maximum length of the final encoding, in bits. - randFunc : callable - An RNG function that accepts as only parameter an int, and returns - a string of random bytes, to be used as salt. - mgf : callable - A mask generation function that accepts two parameters: a string to - use as seed, and the lenth of the mask to generate, in bytes. - sLen : int - Length of the salt, in bytes. - - :Return: An ``emLen`` byte long string that encodes the hash - (with ``emLen = \ceil(emBits/8)``). - - :Raise ValueError: - When digest or salt length are too big. - """ - - emLen = ceil_div(emBits, 8) - - # Bitmask of digits that fill up - lmask = 0 - for i in iter_range(8*emLen-emBits): - lmask = lmask >> 1 | 0x80 - - # Step 1 and 2 have been already done - # Step 3 - if emLen < mhash.digest_size+sLen+2: - raise ValueError("Digest or salt length are too long" - " for given key size.") - # Step 4 - salt = randFunc(sLen) - # Step 5 - m_prime = bchr(0)*8 + mhash.digest() + salt - # Step 6 - h = mhash.new() - h.update(m_prime) - # Step 7 - ps = bchr(0)*(emLen-sLen-mhash.digest_size-2) - # Step 8 - db = ps + bchr(1) + salt - # Step 9 - dbMask = mgf(h.digest(), emLen-mhash.digest_size-1) - # Step 10 - maskedDB = strxor(db, dbMask) - # Step 11 - maskedDB = bchr(bord(maskedDB[0]) & ~lmask) + maskedDB[1:] - # Step 12 - em = maskedDB + h.digest() + bchr(0xBC) - return em - - -def _EMSA_PSS_VERIFY(mhash, em, emBits, mgf, sLen): - """ - Implement the ``EMSA-PSS-VERIFY`` function, as defined - in PKCS#1 v2.1 (RFC3447, 9.1.2). - - ``EMSA-PSS-VERIFY`` actually accepts the message ``M`` as input, - and hash it internally. Here, we expect that the message has already - been hashed instead. - - :Parameters: - mhash : hash object - The hash object that holds the digest of the message to be verified. - em : string - The signature to verify, therefore proving that the sender really - signed the message that was received. - emBits : int - Length of the final encoding (em), in bits. - mgf : callable - A mask generation function that accepts two parameters: a string to - use as seed, and the lenth of the mask to generate, in bytes. - sLen : int - Length of the salt, in bytes. - - :Raise ValueError: - When the encoding is inconsistent, or the digest or salt lengths - are too big. - """ - - emLen = ceil_div(emBits, 8) - - # Bitmask of digits that fill up - lmask = 0 - for i in iter_range(8*emLen-emBits): - lmask = lmask >> 1 | 0x80 - - # Step 1 and 2 have been already done - # Step 3 - if emLen < mhash.digest_size+sLen+2: - raise ValueError("Incorrect signature") - # Step 4 - if ord(em[-1:]) != 0xBC: - raise ValueError("Incorrect signature") - # Step 5 - maskedDB = em[:emLen-mhash.digest_size-1] - h = em[emLen-mhash.digest_size-1:-1] - # Step 6 - if lmask & bord(em[0]): - raise ValueError("Incorrect signature") - # Step 7 - dbMask = mgf(h, emLen-mhash.digest_size-1) - # Step 8 - db = strxor(maskedDB, dbMask) - # Step 9 - db = bchr(bord(db[0]) & ~lmask) + db[1:] - # Step 10 - if not db.startswith(bchr(0)*(emLen-mhash.digest_size-sLen-2) + bchr(1)): - raise ValueError("Incorrect signature") - # Step 11 - if sLen > 0: - salt = db[-sLen:] - else: - salt = b"" - # Step 12 - m_prime = bchr(0)*8 + mhash.digest() + salt - # Step 13 - hobj = mhash.new() - hobj.update(m_prime) - hp = hobj.digest() - # Step 14 - if h != hp: - raise ValueError("Incorrect signature") - - -def new(rsa_key, **kwargs): - """Create an object for making or verifying PKCS#1 PSS signatures. - - :parameter rsa_key: - The RSA key to use for signing or verifying the message. - This is a :class:`Cryptodome.PublicKey.RSA` object. - Signing is only possible when ``rsa_key`` is a **private** RSA key. - :type rsa_key: RSA object - - :Keyword Arguments: - - * *mask_func* (``callable``) -- - A function that returns the mask (as `bytes`). - It must accept two parameters: a seed (as `bytes`) - and the length of the data to return. - - If not specified, it will be the function :func:`MGF1` defined in - `RFC8017 `_ and - combined with the same hash algorithm applied to the - message to sign or verify. - - If you want to use a different function, for instance still :func:`MGF1` - but together with another hash, you can do:: - - from Cryptodome.Hash import SHA256 - from Cryptodome.Signature.pss import MGF1 - mgf = lambda x, y: MGF1(x, y, SHA256) - - * *salt_bytes* (``integer``) -- - Length of the salt, in bytes. - It is a value between 0 and ``emLen - hLen - 2``, where ``emLen`` - is the size of the RSA modulus and ``hLen`` is the size of the digest - applied to the message to sign or verify. - - The salt is generated internally, you don't need to provide it. - - If not specified, the salt length will be ``hLen``. - If it is zero, the signature scheme becomes deterministic. - - Note that in some implementations such as OpenSSL the default - salt length is ``emLen - hLen - 2`` (even though it is not more - secure than ``hLen``). - - * *rand_func* (``callable``) -- - A function that returns random ``bytes``, of the desired length. - The default is :func:`Cryptodome.Random.get_random_bytes`. - - :return: a :class:`PSS_SigScheme` signature object - """ - - mask_func = kwargs.pop("mask_func", None) - salt_len = kwargs.pop("salt_bytes", None) - rand_func = kwargs.pop("rand_func", None) - if rand_func is None: - rand_func = Random.get_random_bytes - if kwargs: - raise ValueError("Unknown keywords: " + str(kwargs.keys())) - return PSS_SigScheme(rsa_key, mask_func, salt_len, rand_func) diff --git a/resources/lib/deps/Cryptodome/Signature/pss.pyi b/resources/lib/deps/Cryptodome/Signature/pss.pyi deleted file mode 100644 index 84a960e..0000000 --- a/resources/lib/deps/Cryptodome/Signature/pss.pyi +++ /dev/null @@ -1,30 +0,0 @@ -from typing import Union, Callable, Optional -from typing_extensions import Protocol - -from Cryptodome.PublicKey.RSA import RsaKey - - -class Hash(Protocol): - def digest(self) -> bytes: ... - def update(self, bytes) -> None: ... - - -class HashModule(Protocol): - @staticmethod - def new(data: Optional[bytes]) -> Hash: ... - - -MaskFunction = Callable[[bytes, int, Union[Hash, HashModule]], bytes] -RndFunction = Callable[[int], bytes] - -class PSS_SigScheme: - def __init__(self, key: RsaKey, mgfunc: MaskFunction, saltLen: int, randfunc: RndFunction) -> None: ... - def can_sign(self) -> bool: ... - def sign(self, msg_hash: Hash) -> bytes: ... - def verify(self, msg_hash: Hash, signature: bytes) -> None: ... - - -MGF1 : MaskFunction -def _EMSA_PSS_ENCODE(mhash: Hash, emBits: int, randFunc: RndFunction, mgf:MaskFunction, sLen: int) -> str: ... -def _EMSA_PSS_VERIFY(mhash: Hash, em: str, emBits: int, mgf: MaskFunction, sLen: int) -> None: ... -def new(rsa_key: RsaKey, **kwargs: Union[MaskFunction, RndFunction, int]) -> PSS_SigScheme: ... diff --git a/resources/lib/deps/Cryptodome/Util/Counter.py b/resources/lib/deps/Cryptodome/Util/Counter.py deleted file mode 100644 index 269b5a7..0000000 --- a/resources/lib/deps/Cryptodome/Util/Counter.py +++ /dev/null @@ -1,79 +0,0 @@ -# -*- coding: ascii -*- -# -# Util/Counter.py : Fast counter for use with CTR-mode ciphers -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -def new(nbits, prefix=b"", suffix=b"", initial_value=1, little_endian=False, allow_wraparound=False): - """Create a stateful counter block function suitable for CTR encryption modes. - - Each call to the function returns the next counter block. - Each counter block is made up by three parts: - - +------+--------------+-------+ - |prefix| counter value|postfix| - +------+--------------+-------+ - - The counter value is incremented by 1 at each call. - - Args: - nbits (integer): - Length of the desired counter value, in bits. It must be a multiple of 8. - prefix (byte string): - The constant prefix of the counter block. By default, no prefix is - used. - suffix (byte string): - The constant postfix of the counter block. By default, no suffix is - used. - initial_value (integer): - The initial value of the counter. Default value is 1. - Its length in bits must not exceed the argument ``nbits``. - little_endian (boolean): - If ``True``, the counter number will be encoded in little endian format. - If ``False`` (default), in big endian format. - allow_wraparound (boolean): - This parameter is ignored. - An ``OverflowError`` exception is always raised when the counter wraps - around to zero. - Returns: - An object that can be passed with the :data:`counter` parameter to a CTR mode - cipher. - - It must hold that *len(prefix) + nbits//8 + len(suffix)* matches the - block size of the underlying block cipher. - """ - - if (nbits % 8) != 0: - raise ValueError("'nbits' must be a multiple of 8") - - iv_bl = initial_value.bit_length() - if iv_bl > nbits: - raise ValueError("Initial value takes %d bits but it is longer than " - "the counter (%d bits)" % - (iv_bl, nbits)) - - # Ignore wraparound - return {"counter_len": nbits // 8, - "prefix": prefix, - "suffix": suffix, - "initial_value": initial_value, - "little_endian": little_endian - } diff --git a/resources/lib/deps/Cryptodome/Util/Counter.pyi b/resources/lib/deps/Cryptodome/Util/Counter.pyi deleted file mode 100644 index fa2ffdd..0000000 --- a/resources/lib/deps/Cryptodome/Util/Counter.pyi +++ /dev/null @@ -1,5 +0,0 @@ -from typing import Optional, Union, Dict - -def new(nbits: int, prefix: Optional[bytes]=..., suffix: Optional[bytes]=..., initial_value: Optional[int]=1, - little_endian: Optional[bool]=False, allow_wraparound: Optional[bool]=False) -> \ - Dict[str, Union[int, bytes, bool]]: ... diff --git a/resources/lib/deps/Cryptodome/Util/Padding.py b/resources/lib/deps/Cryptodome/Util/Padding.py deleted file mode 100644 index b525475..0000000 --- a/resources/lib/deps/Cryptodome/Util/Padding.py +++ /dev/null @@ -1,108 +0,0 @@ -# -# Util/Padding.py : Functions to manage padding -# -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -__all__ = [ 'pad', 'unpad' ] - -from Cryptodome.Util.py3compat import * - - -def pad(data_to_pad, block_size, style='pkcs7'): - """Apply standard padding. - - Args: - data_to_pad (byte string): - The data that needs to be padded. - block_size (integer): - The block boundary to use for padding. The output length is guaranteed - to be a multiple of :data:`block_size`. - style (string): - Padding algorithm. It can be *'pkcs7'* (default), *'iso7816'* or *'x923'*. - - Return: - byte string : the original data with the appropriate padding added at the end. - """ - - padding_len = block_size-len(data_to_pad)%block_size - if style == 'pkcs7': - padding = bchr(padding_len)*padding_len - elif style == 'x923': - padding = bchr(0)*(padding_len-1) + bchr(padding_len) - elif style == 'iso7816': - padding = bchr(128) + bchr(0)*(padding_len-1) - else: - raise ValueError("Unknown padding style") - return data_to_pad + padding - - -def unpad(padded_data, block_size, style='pkcs7'): - """Remove standard padding. - - Args: - padded_data (byte string): - A piece of data with padding that needs to be stripped. - block_size (integer): - The block boundary to use for padding. The input length - must be a multiple of :data:`block_size`. - style (string): - Padding algorithm. It can be *'pkcs7'* (default), *'iso7816'* or *'x923'*. - Return: - byte string : data without padding. - Raises: - ValueError: if the padding is incorrect. - """ - - pdata_len = len(padded_data) - if pdata_len == 0: - raise ValueError("Zero-length input cannot be unpadded") - if pdata_len % block_size: - raise ValueError("Input data is not padded") - if style in ('pkcs7', 'x923'): - padding_len = bord(padded_data[-1]) - if padding_len<1 or padding_len>min(block_size, pdata_len): - raise ValueError("Padding is incorrect.") - if style == 'pkcs7': - if padded_data[-padding_len:]!=bchr(padding_len)*padding_len: - raise ValueError("PKCS#7 padding is incorrect.") - else: - if padded_data[-padding_len:-1]!=bchr(0)*(padding_len-1): - raise ValueError("ANSI X.923 padding is incorrect.") - elif style == 'iso7816': - padding_len = pdata_len - padded_data.rfind(bchr(128)) - if padding_len<1 or padding_len>min(block_size, pdata_len): - raise ValueError("Padding is incorrect.") - if padding_len>1 and padded_data[1-padding_len:]!=bchr(0)*(padding_len-1): - raise ValueError("ISO 7816-4 padding is incorrect.") - else: - raise ValueError("Unknown padding style") - return padded_data[:-padding_len] - diff --git a/resources/lib/deps/Cryptodome/Util/Padding.pyi b/resources/lib/deps/Cryptodome/Util/Padding.pyi deleted file mode 100644 index 4d8d30d..0000000 --- a/resources/lib/deps/Cryptodome/Util/Padding.pyi +++ /dev/null @@ -1,6 +0,0 @@ -from typing import Optional - -__all__ = [ 'pad', 'unpad' ] - -def pad(data_to_pad: bytes, block_size: int, style: Optional[str]='pkcs7') -> bytes: ... -def unpad(padded_data: bytes, block_size: int, style: Optional[str]='pkcs7') -> bytes: ... \ No newline at end of file diff --git a/resources/lib/deps/Cryptodome/Util/RFC1751.py b/resources/lib/deps/Cryptodome/Util/RFC1751.py deleted file mode 100644 index 10859c3..0000000 --- a/resources/lib/deps/Cryptodome/Util/RFC1751.py +++ /dev/null @@ -1,386 +0,0 @@ -# rfc1751.py : Converts between 128-bit strings and a human-readable -# sequence of words, as defined in RFC1751: "A Convention for -# Human-Readable 128-bit Keys", by Daniel L. McDonald. -# -# Part of the Python Cryptography Toolkit -# -# Written by Andrew M. Kuchling and others -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -from __future__ import print_function - -import binascii - -from Cryptodome.Util.py3compat import bord, bchr - -binary = {0: '0000', 1: '0001', 2: '0010', 3: '0011', 4: '0100', 5: '0101', - 6: '0110', 7: '0111', 8: '1000', 9: '1001', 10: '1010', 11: '1011', - 12: '1100', 13: '1101', 14: '1110', 15: '1111'} - - -def _key2bin(s): - "Convert a key into a string of binary digits" - kl = map(lambda x: bord(x), s) - kl = map(lambda x: binary[x >> 4] + binary[x & 15], kl) - return ''.join(kl) - - -def _extract(key, start, length): - """Extract a bitstring(2.x)/bytestring(2.x) from a string of binary digits, and return its - numeric value.""" - - result = 0 - for y in key[start:start+length]: - result = result * 2 + ord(y) - 48 - return result - - -def key_to_english(key): - """Transform an arbitrary key into a string containing English words. - - Example:: - - >>> from Cryptodome.Util.RFC1751 import key_to_english - >>> key_to_english(b'66666666') - 'RAM LOIS GOAD CREW CARE HIT' - - Args: - key (byte string): - The key to convert. Its length must be a multiple of 8. - Return: - A string of English words. - """ - - if len(key) % 8 != 0: - raise ValueError('The length of the key must be a multiple of 8.') - - english = '' - for index in range(0, len(key), 8): # Loop over 8-byte subkeys - subkey = key[index:index + 8] - # Compute the parity of the key - skbin = _key2bin(subkey) - p = 0 - for i in range(0, 64, 2): - p = p + _extract(skbin, i, 2) - # Append parity bits to the subkey - skbin = _key2bin(subkey + bchr((p << 6) & 255)) - for i in range(0, 64, 11): - english = english + wordlist[_extract(skbin, i, 11)] + ' ' - - return english.strip() - - -def english_to_key(s): - """Transform a string into a corresponding key. - - Example:: - - >>> from Cryptodome.Util.RFC1751 import english_to_key - >>> english_to_key('RAM LOIS GOAD CREW CARE HIT') - b'66666666' - - Args: - s (string): the string with the words separated by whitespace; - the number of words must be a multiple of 6. - Return: - A byte string. - """ - - L = s.upper().split() - key = b'' - for index in range(0, len(L), 6): - sublist = L[index:index + 6] - char = 9 * [0] - bits = 0 - for i in sublist: - index = wordlist.index(i) - shift = (8 - (bits + 11) % 8) % 8 - y = index << shift - cl, cc, cr = (y >> 16), (y >> 8) & 0xff, y & 0xff - if (shift > 5): - char[bits >> 3] = char[bits >> 3] | cl - char[(bits >> 3) + 1] = char[(bits >> 3) + 1] | cc - char[(bits >> 3) + 2] = char[(bits >> 3) + 2] | cr - elif shift > -3: - char[bits >> 3] = char[bits >> 3] | cc - char[(bits >> 3) + 1] = char[(bits >> 3) + 1] | cr - else: - char[bits >> 3] = char[bits >> 3] | cr - bits = bits + 11 - - subkey = b'' - for y in char: - subkey = subkey + bchr(y) - - # Check the parity of the resulting key - skbin = _key2bin(subkey) - p = 0 - for i in range(0, 64, 2): - p = p + _extract(skbin, i, 2) - if (p & 3) != _extract(skbin, 64, 2): - raise ValueError("Parity error in resulting key") - key = key + subkey[0:8] - return key - - -wordlist = [ - "A", "ABE", "ACE", "ACT", "AD", "ADA", "ADD", - "AGO", "AID", "AIM", "AIR", "ALL", "ALP", "AM", "AMY", "AN", "ANA", - "AND", "ANN", "ANT", "ANY", "APE", "APS", "APT", "ARC", "ARE", "ARK", - "ARM", "ART", "AS", "ASH", "ASK", "AT", "ATE", "AUG", "AUK", "AVE", - "AWE", "AWK", "AWL", "AWN", "AX", "AYE", "BAD", "BAG", "BAH", "BAM", - "BAN", "BAR", "BAT", "BAY", "BE", "BED", "BEE", "BEG", "BEN", "BET", - "BEY", "BIB", "BID", "BIG", "BIN", "BIT", "BOB", "BOG", "BON", "BOO", - "BOP", "BOW", "BOY", "BUB", "BUD", "BUG", "BUM", "BUN", "BUS", "BUT", - "BUY", "BY", "BYE", "CAB", "CAL", "CAM", "CAN", "CAP", "CAR", "CAT", - "CAW", "COD", "COG", "COL", "CON", "COO", "COP", "COT", "COW", "COY", - "CRY", "CUB", "CUE", "CUP", "CUR", "CUT", "DAB", "DAD", "DAM", "DAN", - "DAR", "DAY", "DEE", "DEL", "DEN", "DES", "DEW", "DID", "DIE", "DIG", - "DIN", "DIP", "DO", "DOE", "DOG", "DON", "DOT", "DOW", "DRY", "DUB", - "DUD", "DUE", "DUG", "DUN", "EAR", "EAT", "ED", "EEL", "EGG", "EGO", - "ELI", "ELK", "ELM", "ELY", "EM", "END", "EST", "ETC", "EVA", "EVE", - "EWE", "EYE", "FAD", "FAN", "FAR", "FAT", "FAY", "FED", "FEE", "FEW", - "FIB", "FIG", "FIN", "FIR", "FIT", "FLO", "FLY", "FOE", "FOG", "FOR", - "FRY", "FUM", "FUN", "FUR", "GAB", "GAD", "GAG", "GAL", "GAM", "GAP", - "GAS", "GAY", "GEE", "GEL", "GEM", "GET", "GIG", "GIL", "GIN", "GO", - "GOT", "GUM", "GUN", "GUS", "GUT", "GUY", "GYM", "GYP", "HA", "HAD", - "HAL", "HAM", "HAN", "HAP", "HAS", "HAT", "HAW", "HAY", "HE", "HEM", - "HEN", "HER", "HEW", "HEY", "HI", "HID", "HIM", "HIP", "HIS", "HIT", - "HO", "HOB", "HOC", "HOE", "HOG", "HOP", "HOT", "HOW", "HUB", "HUE", - "HUG", "HUH", "HUM", "HUT", "I", "ICY", "IDA", "IF", "IKE", "ILL", - "INK", "INN", "IO", "ION", "IQ", "IRA", "IRE", "IRK", "IS", "IT", - "ITS", "IVY", "JAB", "JAG", "JAM", "JAN", "JAR", "JAW", "JAY", "JET", - "JIG", "JIM", "JO", "JOB", "JOE", "JOG", "JOT", "JOY", "JUG", "JUT", - "KAY", "KEG", "KEN", "KEY", "KID", "KIM", "KIN", "KIT", "LA", "LAB", - "LAC", "LAD", "LAG", "LAM", "LAP", "LAW", "LAY", "LEA", "LED", "LEE", - "LEG", "LEN", "LEO", "LET", "LEW", "LID", "LIE", "LIN", "LIP", "LIT", - "LO", "LOB", "LOG", "LOP", "LOS", "LOT", "LOU", "LOW", "LOY", "LUG", - "LYE", "MA", "MAC", "MAD", "MAE", "MAN", "MAO", "MAP", "MAT", "MAW", - "MAY", "ME", "MEG", "MEL", "MEN", "MET", "MEW", "MID", "MIN", "MIT", - "MOB", "MOD", "MOE", "MOO", "MOP", "MOS", "MOT", "MOW", "MUD", "MUG", - "MUM", "MY", "NAB", "NAG", "NAN", "NAP", "NAT", "NAY", "NE", "NED", - "NEE", "NET", "NEW", "NIB", "NIL", "NIP", "NIT", "NO", "NOB", "NOD", - "NON", "NOR", "NOT", "NOV", "NOW", "NU", "NUN", "NUT", "O", "OAF", - "OAK", "OAR", "OAT", "ODD", "ODE", "OF", "OFF", "OFT", "OH", "OIL", - "OK", "OLD", "ON", "ONE", "OR", "ORB", "ORE", "ORR", "OS", "OTT", - "OUR", "OUT", "OVA", "OW", "OWE", "OWL", "OWN", "OX", "PA", "PAD", - "PAL", "PAM", "PAN", "PAP", "PAR", "PAT", "PAW", "PAY", "PEA", "PEG", - "PEN", "PEP", "PER", "PET", "PEW", "PHI", "PI", "PIE", "PIN", "PIT", - "PLY", "PO", "POD", "POE", "POP", "POT", "POW", "PRO", "PRY", "PUB", - "PUG", "PUN", "PUP", "PUT", "QUO", "RAG", "RAM", "RAN", "RAP", "RAT", - "RAW", "RAY", "REB", "RED", "REP", "RET", "RIB", "RID", "RIG", "RIM", - "RIO", "RIP", "ROB", "ROD", "ROE", "RON", "ROT", "ROW", "ROY", "RUB", - "RUE", "RUG", "RUM", "RUN", "RYE", "SAC", "SAD", "SAG", "SAL", "SAM", - "SAN", "SAP", "SAT", "SAW", "SAY", "SEA", "SEC", "SEE", "SEN", "SET", - "SEW", "SHE", "SHY", "SIN", "SIP", "SIR", "SIS", "SIT", "SKI", "SKY", - "SLY", "SO", "SOB", "SOD", "SON", "SOP", "SOW", "SOY", "SPA", "SPY", - "SUB", "SUD", "SUE", "SUM", "SUN", "SUP", "TAB", "TAD", "TAG", "TAN", - "TAP", "TAR", "TEA", "TED", "TEE", "TEN", "THE", "THY", "TIC", "TIE", - "TIM", "TIN", "TIP", "TO", "TOE", "TOG", "TOM", "TON", "TOO", "TOP", - "TOW", "TOY", "TRY", "TUB", "TUG", "TUM", "TUN", "TWO", "UN", "UP", - "US", "USE", "VAN", "VAT", "VET", "VIE", "WAD", "WAG", "WAR", "WAS", - "WAY", "WE", "WEB", "WED", "WEE", "WET", "WHO", "WHY", "WIN", "WIT", - "WOK", "WON", "WOO", "WOW", "WRY", "WU", "YAM", "YAP", "YAW", "YE", - "YEA", "YES", "YET", "YOU", "ABED", "ABEL", "ABET", "ABLE", "ABUT", - "ACHE", "ACID", "ACME", "ACRE", "ACTA", "ACTS", "ADAM", "ADDS", - "ADEN", "AFAR", "AFRO", "AGEE", "AHEM", "AHOY", "AIDA", "AIDE", - "AIDS", "AIRY", "AJAR", "AKIN", "ALAN", "ALEC", "ALGA", "ALIA", - "ALLY", "ALMA", "ALOE", "ALSO", "ALTO", "ALUM", "ALVA", "AMEN", - "AMES", "AMID", "AMMO", "AMOK", "AMOS", "AMRA", "ANDY", "ANEW", - "ANNA", "ANNE", "ANTE", "ANTI", "AQUA", "ARAB", "ARCH", "AREA", - "ARGO", "ARID", "ARMY", "ARTS", "ARTY", "ASIA", "ASKS", "ATOM", - "AUNT", "AURA", "AUTO", "AVER", "AVID", "AVIS", "AVON", "AVOW", - "AWAY", "AWRY", "BABE", "BABY", "BACH", "BACK", "BADE", "BAIL", - "BAIT", "BAKE", "BALD", "BALE", "BALI", "BALK", "BALL", "BALM", - "BAND", "BANE", "BANG", "BANK", "BARB", "BARD", "BARE", "BARK", - "BARN", "BARR", "BASE", "BASH", "BASK", "BASS", "BATE", "BATH", - "BAWD", "BAWL", "BEAD", "BEAK", "BEAM", "BEAN", "BEAR", "BEAT", - "BEAU", "BECK", "BEEF", "BEEN", "BEER", - "BEET", "BELA", "BELL", "BELT", "BEND", "BENT", "BERG", "BERN", - "BERT", "BESS", "BEST", "BETA", "BETH", "BHOY", "BIAS", "BIDE", - "BIEN", "BILE", "BILK", "BILL", "BIND", "BING", "BIRD", "BITE", - "BITS", "BLAB", "BLAT", "BLED", "BLEW", "BLOB", "BLOC", "BLOT", - "BLOW", "BLUE", "BLUM", "BLUR", "BOAR", "BOAT", "BOCA", "BOCK", - "BODE", "BODY", "BOGY", "BOHR", "BOIL", "BOLD", "BOLO", "BOLT", - "BOMB", "BONA", "BOND", "BONE", "BONG", "BONN", "BONY", "BOOK", - "BOOM", "BOON", "BOOT", "BORE", "BORG", "BORN", "BOSE", "BOSS", - "BOTH", "BOUT", "BOWL", "BOYD", "BRAD", "BRAE", "BRAG", "BRAN", - "BRAY", "BRED", "BREW", "BRIG", "BRIM", "BROW", "BUCK", "BUDD", - "BUFF", "BULB", "BULK", "BULL", "BUNK", "BUNT", "BUOY", "BURG", - "BURL", "BURN", "BURR", "BURT", "BURY", "BUSH", "BUSS", "BUST", - "BUSY", "BYTE", "CADY", "CAFE", "CAGE", "CAIN", "CAKE", "CALF", - "CALL", "CALM", "CAME", "CANE", "CANT", "CARD", "CARE", "CARL", - "CARR", "CART", "CASE", "CASH", "CASK", "CAST", "CAVE", "CEIL", - "CELL", "CENT", "CERN", "CHAD", "CHAR", "CHAT", "CHAW", "CHEF", - "CHEN", "CHEW", "CHIC", "CHIN", "CHOU", "CHOW", "CHUB", "CHUG", - "CHUM", "CITE", "CITY", "CLAD", "CLAM", "CLAN", "CLAW", "CLAY", - "CLOD", "CLOG", "CLOT", "CLUB", "CLUE", "COAL", "COAT", "COCA", - "COCK", "COCO", "CODA", "CODE", "CODY", "COED", "COIL", "COIN", - "COKE", "COLA", "COLD", "COLT", "COMA", "COMB", "COME", "COOK", - "COOL", "COON", "COOT", "CORD", "CORE", "CORK", "CORN", "COST", - "COVE", "COWL", "CRAB", "CRAG", "CRAM", "CRAY", "CREW", "CRIB", - "CROW", "CRUD", "CUBA", "CUBE", "CUFF", "CULL", "CULT", "CUNY", - "CURB", "CURD", "CURE", "CURL", "CURT", "CUTS", "DADE", "DALE", - "DAME", "DANA", "DANE", "DANG", "DANK", "DARE", "DARK", "DARN", - "DART", "DASH", "DATA", "DATE", "DAVE", "DAVY", "DAWN", "DAYS", - "DEAD", "DEAF", "DEAL", "DEAN", "DEAR", "DEBT", "DECK", "DEED", - "DEEM", "DEER", "DEFT", "DEFY", "DELL", "DENT", "DENY", "DESK", - "DIAL", "DICE", "DIED", "DIET", "DIME", "DINE", "DING", "DINT", - "DIRE", "DIRT", "DISC", "DISH", "DISK", "DIVE", "DOCK", "DOES", - "DOLE", "DOLL", "DOLT", "DOME", "DONE", "DOOM", "DOOR", "DORA", - "DOSE", "DOTE", "DOUG", "DOUR", "DOVE", "DOWN", "DRAB", "DRAG", - "DRAM", "DRAW", "DREW", "DRUB", "DRUG", "DRUM", "DUAL", "DUCK", - "DUCT", "DUEL", "DUET", "DUKE", "DULL", "DUMB", "DUNE", "DUNK", - "DUSK", "DUST", "DUTY", "EACH", "EARL", "EARN", "EASE", "EAST", - "EASY", "EBEN", "ECHO", "EDDY", "EDEN", "EDGE", "EDGY", "EDIT", - "EDNA", "EGAN", "ELAN", "ELBA", "ELLA", "ELSE", "EMIL", "EMIT", - "EMMA", "ENDS", "ERIC", "EROS", "EVEN", "EVER", "EVIL", "EYED", - "FACE", "FACT", "FADE", "FAIL", "FAIN", "FAIR", "FAKE", "FALL", - "FAME", "FANG", "FARM", "FAST", "FATE", "FAWN", "FEAR", "FEAT", - "FEED", "FEEL", "FEET", "FELL", "FELT", "FEND", "FERN", "FEST", - "FEUD", "FIEF", "FIGS", "FILE", "FILL", "FILM", "FIND", "FINE", - "FINK", "FIRE", "FIRM", "FISH", "FISK", "FIST", "FITS", "FIVE", - "FLAG", "FLAK", "FLAM", "FLAT", "FLAW", "FLEA", "FLED", "FLEW", - "FLIT", "FLOC", "FLOG", "FLOW", "FLUB", "FLUE", "FOAL", "FOAM", - "FOGY", "FOIL", "FOLD", "FOLK", "FOND", "FONT", "FOOD", "FOOL", - "FOOT", "FORD", "FORE", "FORK", "FORM", "FORT", "FOSS", "FOUL", - "FOUR", "FOWL", "FRAU", "FRAY", "FRED", "FREE", "FRET", "FREY", - "FROG", "FROM", "FUEL", "FULL", "FUME", "FUND", "FUNK", "FURY", - "FUSE", "FUSS", "GAFF", "GAGE", "GAIL", "GAIN", "GAIT", "GALA", - "GALE", "GALL", "GALT", "GAME", "GANG", "GARB", "GARY", "GASH", - "GATE", "GAUL", "GAUR", "GAVE", "GAWK", "GEAR", "GELD", "GENE", - "GENT", "GERM", "GETS", "GIBE", "GIFT", "GILD", "GILL", "GILT", - "GINA", "GIRD", "GIRL", "GIST", "GIVE", "GLAD", "GLEE", "GLEN", - "GLIB", "GLOB", "GLOM", "GLOW", "GLUE", "GLUM", "GLUT", "GOAD", - "GOAL", "GOAT", "GOER", "GOES", "GOLD", "GOLF", "GONE", "GONG", - "GOOD", "GOOF", "GORE", "GORY", "GOSH", "GOUT", "GOWN", "GRAB", - "GRAD", "GRAY", "GREG", "GREW", "GREY", "GRID", "GRIM", "GRIN", - "GRIT", "GROW", "GRUB", "GULF", "GULL", "GUNK", "GURU", "GUSH", - "GUST", "GWEN", "GWYN", "HAAG", "HAAS", "HACK", "HAIL", "HAIR", - "HALE", "HALF", "HALL", "HALO", "HALT", "HAND", "HANG", "HANK", - "HANS", "HARD", "HARK", "HARM", "HART", "HASH", "HAST", "HATE", - "HATH", "HAUL", "HAVE", "HAWK", "HAYS", "HEAD", "HEAL", "HEAR", - "HEAT", "HEBE", "HECK", "HEED", "HEEL", "HEFT", "HELD", "HELL", - "HELM", "HERB", "HERD", "HERE", "HERO", "HERS", "HESS", "HEWN", - "HICK", "HIDE", "HIGH", "HIKE", "HILL", "HILT", "HIND", "HINT", - "HIRE", "HISS", "HIVE", "HOBO", "HOCK", "HOFF", "HOLD", "HOLE", - "HOLM", "HOLT", "HOME", "HONE", "HONK", "HOOD", "HOOF", "HOOK", - "HOOT", "HORN", "HOSE", "HOST", "HOUR", "HOVE", "HOWE", "HOWL", - "HOYT", "HUCK", "HUED", "HUFF", "HUGE", "HUGH", "HUGO", "HULK", - "HULL", "HUNK", "HUNT", "HURD", "HURL", "HURT", "HUSH", "HYDE", - "HYMN", "IBIS", "ICON", "IDEA", "IDLE", "IFFY", "INCA", "INCH", - "INTO", "IONS", "IOTA", "IOWA", "IRIS", "IRMA", "IRON", "ISLE", - "ITCH", "ITEM", "IVAN", "JACK", "JADE", "JAIL", "JAKE", "JANE", - "JAVA", "JEAN", "JEFF", "JERK", "JESS", "JEST", "JIBE", "JILL", - "JILT", "JIVE", "JOAN", "JOBS", "JOCK", "JOEL", "JOEY", "JOHN", - "JOIN", "JOKE", "JOLT", "JOVE", "JUDD", "JUDE", "JUDO", "JUDY", - "JUJU", "JUKE", "JULY", "JUNE", "JUNK", "JUNO", "JURY", "JUST", - "JUTE", "KAHN", "KALE", "KANE", "KANT", "KARL", "KATE", "KEEL", - "KEEN", "KENO", "KENT", "KERN", "KERR", "KEYS", "KICK", "KILL", - "KIND", "KING", "KIRK", "KISS", "KITE", "KLAN", "KNEE", "KNEW", - "KNIT", "KNOB", "KNOT", "KNOW", "KOCH", "KONG", "KUDO", "KURD", - "KURT", "KYLE", "LACE", "LACK", "LACY", "LADY", "LAID", "LAIN", - "LAIR", "LAKE", "LAMB", "LAME", "LAND", "LANE", "LANG", "LARD", - "LARK", "LASS", "LAST", "LATE", "LAUD", "LAVA", "LAWN", "LAWS", - "LAYS", "LEAD", "LEAF", "LEAK", "LEAN", "LEAR", "LEEK", "LEER", - "LEFT", "LEND", "LENS", "LENT", "LEON", "LESK", "LESS", "LEST", - "LETS", "LIAR", "LICE", "LICK", "LIED", "LIEN", "LIES", "LIEU", - "LIFE", "LIFT", "LIKE", "LILA", "LILT", "LILY", "LIMA", "LIMB", - "LIME", "LIND", "LINE", "LINK", "LINT", "LION", "LISA", "LIST", - "LIVE", "LOAD", "LOAF", "LOAM", "LOAN", "LOCK", "LOFT", "LOGE", - "LOIS", "LOLA", "LONE", "LONG", "LOOK", "LOON", "LOOT", "LORD", - "LORE", "LOSE", "LOSS", "LOST", "LOUD", "LOVE", "LOWE", "LUCK", - "LUCY", "LUGE", "LUKE", "LULU", "LUND", "LUNG", "LURA", "LURE", - "LURK", "LUSH", "LUST", "LYLE", "LYNN", "LYON", "LYRA", "MACE", - "MADE", "MAGI", "MAID", "MAIL", "MAIN", "MAKE", "MALE", "MALI", - "MALL", "MALT", "MANA", "MANN", "MANY", "MARC", "MARE", "MARK", - "MARS", "MART", "MARY", "MASH", "MASK", "MASS", "MAST", "MATE", - "MATH", "MAUL", "MAYO", "MEAD", "MEAL", "MEAN", "MEAT", "MEEK", - "MEET", "MELD", "MELT", "MEMO", "MEND", "MENU", "MERT", "MESH", - "MESS", "MICE", "MIKE", "MILD", "MILE", "MILK", "MILL", "MILT", - "MIMI", "MIND", "MINE", "MINI", "MINK", "MINT", "MIRE", "MISS", - "MIST", "MITE", "MITT", "MOAN", "MOAT", "MOCK", "MODE", "MOLD", - "MOLE", "MOLL", "MOLT", "MONA", "MONK", "MONT", "MOOD", "MOON", - "MOOR", "MOOT", "MORE", "MORN", "MORT", "MOSS", "MOST", "MOTH", - "MOVE", "MUCH", "MUCK", "MUDD", "MUFF", "MULE", "MULL", "MURK", - "MUSH", "MUST", "MUTE", "MUTT", "MYRA", "MYTH", "NAGY", "NAIL", - "NAIR", "NAME", "NARY", "NASH", "NAVE", "NAVY", "NEAL", "NEAR", - "NEAT", "NECK", "NEED", "NEIL", "NELL", "NEON", "NERO", "NESS", - "NEST", "NEWS", "NEWT", "NIBS", "NICE", "NICK", "NILE", "NINA", - "NINE", "NOAH", "NODE", "NOEL", "NOLL", "NONE", "NOOK", "NOON", - "NORM", "NOSE", "NOTE", "NOUN", "NOVA", "NUDE", "NULL", "NUMB", - "OATH", "OBEY", "OBOE", "ODIN", "OHIO", "OILY", "OINT", "OKAY", - "OLAF", "OLDY", "OLGA", "OLIN", "OMAN", "OMEN", "OMIT", "ONCE", - "ONES", "ONLY", "ONTO", "ONUS", "ORAL", "ORGY", "OSLO", "OTIS", - "OTTO", "OUCH", "OUST", "OUTS", "OVAL", "OVEN", "OVER", "OWLY", - "OWNS", "QUAD", "QUIT", "QUOD", "RACE", "RACK", "RACY", "RAFT", - "RAGE", "RAID", "RAIL", "RAIN", "RAKE", "RANK", "RANT", "RARE", - "RASH", "RATE", "RAVE", "RAYS", "READ", "REAL", "REAM", "REAR", - "RECK", "REED", "REEF", "REEK", "REEL", "REID", "REIN", "RENA", - "REND", "RENT", "REST", "RICE", "RICH", "RICK", "RIDE", "RIFT", - "RILL", "RIME", "RING", "RINK", "RISE", "RISK", "RITE", "ROAD", - "ROAM", "ROAR", "ROBE", "ROCK", "RODE", "ROIL", "ROLL", "ROME", - "ROOD", "ROOF", "ROOK", "ROOM", "ROOT", "ROSA", "ROSE", "ROSS", - "ROSY", "ROTH", "ROUT", "ROVE", "ROWE", "ROWS", "RUBE", "RUBY", - "RUDE", "RUDY", "RUIN", "RULE", "RUNG", "RUNS", "RUNT", "RUSE", - "RUSH", "RUSK", "RUSS", "RUST", "RUTH", "SACK", "SAFE", "SAGE", - "SAID", "SAIL", "SALE", "SALK", "SALT", "SAME", "SAND", "SANE", - "SANG", "SANK", "SARA", "SAUL", "SAVE", "SAYS", "SCAN", "SCAR", - "SCAT", "SCOT", "SEAL", "SEAM", "SEAR", "SEAT", "SEED", "SEEK", - "SEEM", "SEEN", "SEES", "SELF", "SELL", "SEND", "SENT", "SETS", - "SEWN", "SHAG", "SHAM", "SHAW", "SHAY", "SHED", "SHIM", "SHIN", - "SHOD", "SHOE", "SHOT", "SHOW", "SHUN", "SHUT", "SICK", "SIDE", - "SIFT", "SIGH", "SIGN", "SILK", "SILL", "SILO", "SILT", "SINE", - "SING", "SINK", "SIRE", "SITE", "SITS", "SITU", "SKAT", "SKEW", - "SKID", "SKIM", "SKIN", "SKIT", "SLAB", "SLAM", "SLAT", "SLAY", - "SLED", "SLEW", "SLID", "SLIM", "SLIT", "SLOB", "SLOG", "SLOT", - "SLOW", "SLUG", "SLUM", "SLUR", "SMOG", "SMUG", "SNAG", "SNOB", - "SNOW", "SNUB", "SNUG", "SOAK", "SOAR", "SOCK", "SODA", "SOFA", - "SOFT", "SOIL", "SOLD", "SOME", "SONG", "SOON", "SOOT", "SORE", - "SORT", "SOUL", "SOUR", "SOWN", "STAB", "STAG", "STAN", "STAR", - "STAY", "STEM", "STEW", "STIR", "STOW", "STUB", "STUN", "SUCH", - "SUDS", "SUIT", "SULK", "SUMS", "SUNG", "SUNK", "SURE", "SURF", - "SWAB", "SWAG", "SWAM", "SWAN", "SWAT", "SWAY", "SWIM", "SWUM", - "TACK", "TACT", "TAIL", "TAKE", "TALE", "TALK", "TALL", "TANK", - "TASK", "TATE", "TAUT", "TEAL", "TEAM", "TEAR", "TECH", "TEEM", - "TEEN", "TEET", "TELL", "TEND", "TENT", "TERM", "TERN", "TESS", - "TEST", "THAN", "THAT", "THEE", "THEM", "THEN", "THEY", "THIN", - "THIS", "THUD", "THUG", "TICK", "TIDE", "TIDY", "TIED", "TIER", - "TILE", "TILL", "TILT", "TIME", "TINA", "TINE", "TINT", "TINY", - "TIRE", "TOAD", "TOGO", "TOIL", "TOLD", "TOLL", "TONE", "TONG", - "TONY", "TOOK", "TOOL", "TOOT", "TORE", "TORN", "TOTE", "TOUR", - "TOUT", "TOWN", "TRAG", "TRAM", "TRAY", "TREE", "TREK", "TRIG", - "TRIM", "TRIO", "TROD", "TROT", "TROY", "TRUE", "TUBA", "TUBE", - "TUCK", "TUFT", "TUNA", "TUNE", "TUNG", "TURF", "TURN", "TUSK", - "TWIG", "TWIN", "TWIT", "ULAN", "UNIT", "URGE", "USED", "USER", - "USES", "UTAH", "VAIL", "VAIN", "VALE", "VARY", "VASE", "VAST", - "VEAL", "VEDA", "VEIL", "VEIN", "VEND", "VENT", "VERB", "VERY", - "VETO", "VICE", "VIEW", "VINE", "VISE", "VOID", "VOLT", "VOTE", - "WACK", "WADE", "WAGE", "WAIL", "WAIT", "WAKE", "WALE", "WALK", - "WALL", "WALT", "WAND", "WANE", "WANG", "WANT", "WARD", "WARM", - "WARN", "WART", "WASH", "WAST", "WATS", "WATT", "WAVE", "WAVY", - "WAYS", "WEAK", "WEAL", "WEAN", "WEAR", "WEED", "WEEK", "WEIR", - "WELD", "WELL", "WELT", "WENT", "WERE", "WERT", "WEST", "WHAM", - "WHAT", "WHEE", "WHEN", "WHET", "WHOA", "WHOM", "WICK", "WIFE", - "WILD", "WILL", "WIND", "WINE", "WING", "WINK", "WINO", "WIRE", - "WISE", "WISH", "WITH", "WOLF", "WONT", "WOOD", "WOOL", "WORD", - "WORE", "WORK", "WORM", "WORN", "WOVE", "WRIT", "WYNN", "YALE", - "YANG", "YANK", "YARD", "YARN", "YAWL", "YAWN", "YEAH", "YEAR", - "YELL", "YOGA", "YOKE" ] diff --git a/resources/lib/deps/Cryptodome/Util/RFC1751.pyi b/resources/lib/deps/Cryptodome/Util/RFC1751.pyi deleted file mode 100644 index 6ad07ff..0000000 --- a/resources/lib/deps/Cryptodome/Util/RFC1751.pyi +++ /dev/null @@ -1,7 +0,0 @@ -from typing import Dict, List - -binary: Dict[int, str] -wordlist: List[str] - -def key_to_english(key: bytes) -> str: ... -def english_to_key(s: str) -> bytes: ... diff --git a/resources/lib/deps/Cryptodome/Util/__init__.py b/resources/lib/deps/Cryptodome/Util/__init__.py deleted file mode 100644 index 1862b82..0000000 --- a/resources/lib/deps/Cryptodome/Util/__init__.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Miscellaneous modules - -Contains useful modules that don't belong into any of the -other Cryptodome.* subpackages. - -======================== ============================================= -Module Description -======================== ============================================= -`Cryptodome.Util.number` Number-theoretic functions (primality testing, etc.) -`Cryptodome.Util.Counter` Fast counter functions for CTR cipher modes. -`Cryptodome.Util.RFC1751` Converts between 128-bit keys and human-readable - strings of words. -`Cryptodome.Util.asn1` Minimal support for ASN.1 DER encoding -`Cryptodome.Util.Padding` Set of functions for adding and removing padding. -======================== ============================================= - -:undocumented: _galois, _number_new, cpuid, py3compat, _raw_api -""" - -__all__ = ['RFC1751', 'number', 'strxor', 'asn1', 'Counter', 'Padding'] - diff --git a/resources/lib/deps/Cryptodome/Util/_cpu_features.py b/resources/lib/deps/Cryptodome/Util/_cpu_features.py deleted file mode 100644 index 4794a02..0000000 --- a/resources/lib/deps/Cryptodome/Util/_cpu_features.py +++ /dev/null @@ -1,46 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2018, Helder Eijs -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from Cryptodome.Util._raw_api import load_pycryptodome_raw_lib - - -_raw_cpuid_lib = load_pycryptodome_raw_lib("Cryptodome.Util._cpuid_c", - """ - int have_aes_ni(void); - int have_clmul(void); - """) - - -def have_aes_ni(): - return _raw_cpuid_lib.have_aes_ni() - - -def have_clmul(): - return _raw_cpuid_lib.have_clmul() diff --git a/resources/lib/deps/Cryptodome/Util/_cpu_features.pyi b/resources/lib/deps/Cryptodome/Util/_cpu_features.pyi deleted file mode 100644 index 10e669e..0000000 --- a/resources/lib/deps/Cryptodome/Util/_cpu_features.pyi +++ /dev/null @@ -1,2 +0,0 @@ -def have_aes_ni() -> int: ... -def have_clmul() -> int: ... diff --git a/resources/lib/deps/Cryptodome/Util/_cpuid_c.abi3.so b/resources/lib/deps/Cryptodome/Util/_cpuid_c.abi3.so deleted file mode 100755 index 51e31b74f5844907c19e0fcba72a1662b897402e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19304 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 -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import os - - -def pycryptodome_filename(dir_comps, filename): - """Return the complete file name for the module - - dir_comps : list of string - The list of directory names in the PyCryptodome package. - The first element must be "Cryptodome". - - filename : string - The filename (inclusing extension) in the target directory. - """ - - if dir_comps[0] != "Cryptodome": - raise ValueError("Only available for modules under 'Cryptodome'") - - dir_comps = list(dir_comps[1:]) + [filename] - - util_lib, _ = os.path.split(os.path.abspath(__file__)) - root_lib = os.path.join(util_lib, "..") - - return os.path.join(root_lib, *dir_comps) - diff --git a/resources/lib/deps/Cryptodome/Util/_file_system.pyi b/resources/lib/deps/Cryptodome/Util/_file_system.pyi deleted file mode 100644 index d54a126..0000000 --- a/resources/lib/deps/Cryptodome/Util/_file_system.pyi +++ /dev/null @@ -1,4 +0,0 @@ -from typing import List - - -def pycryptodome_filename(dir_comps: List[str], filename: str) -> str: ... \ No newline at end of file diff --git a/resources/lib/deps/Cryptodome/Util/_raw_api.py b/resources/lib/deps/Cryptodome/Util/_raw_api.py deleted file mode 100644 index cd64ac8..0000000 --- a/resources/lib/deps/Cryptodome/Util/_raw_api.py +++ /dev/null @@ -1,325 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -import os -import abc -import sys -from Cryptodome.Util.py3compat import byte_string -from Cryptodome.Util._file_system import pycryptodome_filename - -# -# List of file suffixes for Python extensions -# -if sys.version_info[0] < 3: - - import imp - extension_suffixes = [] - for ext, mod, typ in imp.get_suffixes(): - if typ == imp.C_EXTENSION: - extension_suffixes.append(ext) - -else: - - from importlib import machinery - extension_suffixes = machinery.EXTENSION_SUFFIXES - -# Which types with buffer interface we support (apart from byte strings) -_buffer_type = (bytearray, memoryview) - - -class _VoidPointer(object): - @abc.abstractmethod - def get(self): - """Return the memory location we point to""" - return - - @abc.abstractmethod - def address_of(self): - """Return a raw pointer to this pointer""" - return - - -try: - # Starting from v2.18, pycparser (used by cffi for in-line ABI mode) - # stops working correctly when PYOPTIMIZE==2 or the parameter -OO is - # passed. In that case, we fall back to ctypes. - # Note that PyPy ships with an old version of pycparser so we can keep - # using cffi there. - # See https://github.com/Legrandin/pycryptodome/issues/228 - if '__pypy__' not in sys.builtin_module_names and sys.flags.optimize == 2: - raise ImportError("CFFI with optimize=2 fails due to pycparser bug.") - - # cffi still uses PyUnicode_GetSize, which was removed in Python 3.12 - # thus leading to a crash on cffi.dlopen() - # See https://groups.google.com/u/1/g/python-cffi/c/oZkOIZ_zi5k - if sys.version_info >= (3, 12) and os.name == "nt": - raise ImportError("CFFI is not compatible with Python 3.12 on Windows") - - from cffi import FFI - - ffi = FFI() - null_pointer = ffi.NULL - uint8_t_type = ffi.typeof(ffi.new("const uint8_t*")) - - _Array = ffi.new("uint8_t[1]").__class__.__bases__ - - def load_lib(name, cdecl): - """Load a shared library and return a handle to it. - - @name, either an absolute path or the name of a library - in the system search path. - - @cdecl, the C function declarations. - """ - - if hasattr(ffi, "RTLD_DEEPBIND") and not os.getenv('PYCRYPTODOME_DISABLE_DEEPBIND'): - lib = ffi.dlopen(name, ffi.RTLD_DEEPBIND) - else: - lib = ffi.dlopen(name) - ffi.cdef(cdecl) - return lib - - def c_ulong(x): - """Convert a Python integer to unsigned long""" - return x - - c_ulonglong = c_ulong - c_uint = c_ulong - c_ubyte = c_ulong - - def c_size_t(x): - """Convert a Python integer to size_t""" - return x - - def create_string_buffer(init_or_size, size=None): - """Allocate the given amount of bytes (initially set to 0)""" - - if isinstance(init_or_size, bytes): - size = max(len(init_or_size) + 1, size) - result = ffi.new("uint8_t[]", size) - result[:] = init_or_size - else: - if size: - raise ValueError("Size must be specified once only") - result = ffi.new("uint8_t[]", init_or_size) - return result - - def get_c_string(c_string): - """Convert a C string into a Python byte sequence""" - return ffi.string(c_string) - - def get_raw_buffer(buf): - """Convert a C buffer into a Python byte sequence""" - return ffi.buffer(buf)[:] - - def c_uint8_ptr(data): - if isinstance(data, _buffer_type): - # This only works for cffi >= 1.7 - return ffi.cast(uint8_t_type, ffi.from_buffer(data)) - elif byte_string(data) or isinstance(data, _Array): - return data - else: - raise TypeError("Object type %s cannot be passed to C code" % type(data)) - - class VoidPointer_cffi(_VoidPointer): - """Model a newly allocated pointer to void""" - - def __init__(self): - self._pp = ffi.new("void *[1]") - - def get(self): - return self._pp[0] - - def address_of(self): - return self._pp - - def VoidPointer(): - return VoidPointer_cffi() - - backend = "cffi" - -except ImportError: - - import ctypes - from ctypes import (CDLL, c_void_p, byref, c_ulong, c_ulonglong, c_size_t, - create_string_buffer, c_ubyte, c_uint) - from ctypes.util import find_library - from ctypes import Array as _Array - - null_pointer = None - cached_architecture = [] - - def c_ubyte(c): - if not (0 <= c < 256): - raise OverflowError() - return ctypes.c_ubyte(c) - - def load_lib(name, cdecl): - if not cached_architecture: - # platform.architecture() creates a subprocess, so caching the - # result makes successive imports faster. - import platform - cached_architecture[:] = platform.architecture() - bits, linkage = cached_architecture - if "." not in name and not linkage.startswith("Win"): - full_name = find_library(name) - if full_name is None: - raise OSError("Cannot load library '%s'" % name) - name = full_name - return CDLL(name) - - def get_c_string(c_string): - return c_string.value - - def get_raw_buffer(buf): - return buf.raw - - # ---- Get raw pointer --- - - _c_ssize_t = ctypes.c_ssize_t - - _PyBUF_SIMPLE = 0 - _PyObject_GetBuffer = ctypes.pythonapi.PyObject_GetBuffer - _PyBuffer_Release = ctypes.pythonapi.PyBuffer_Release - _py_object = ctypes.py_object - _c_ssize_p = ctypes.POINTER(_c_ssize_t) - - # See Include/object.h for CPython - # and https://github.com/pallets/click/blob/master/src/click/_winconsole.py - class _Py_buffer(ctypes.Structure): - _fields_ = [ - ('buf', c_void_p), - ('obj', ctypes.py_object), - ('len', _c_ssize_t), - ('itemsize', _c_ssize_t), - ('readonly', ctypes.c_int), - ('ndim', ctypes.c_int), - ('format', ctypes.c_char_p), - ('shape', _c_ssize_p), - ('strides', _c_ssize_p), - ('suboffsets', _c_ssize_p), - ('internal', c_void_p) - ] - - # Extra field for CPython 2.6/2.7 - if sys.version_info[0] == 2: - _fields_.insert(-1, ('smalltable', _c_ssize_t * 2)) - - def c_uint8_ptr(data): - if byte_string(data) or isinstance(data, _Array): - return data - elif isinstance(data, _buffer_type): - obj = _py_object(data) - buf = _Py_buffer() - _PyObject_GetBuffer(obj, byref(buf), _PyBUF_SIMPLE) - try: - buffer_type = ctypes.c_ubyte * buf.len - return buffer_type.from_address(buf.buf) - finally: - _PyBuffer_Release(byref(buf)) - else: - raise TypeError("Object type %s cannot be passed to C code" % type(data)) - - # --- - - class VoidPointer_ctypes(_VoidPointer): - """Model a newly allocated pointer to void""" - - def __init__(self): - self._p = c_void_p() - - def get(self): - return self._p - - def address_of(self): - return byref(self._p) - - def VoidPointer(): - return VoidPointer_ctypes() - - backend = "ctypes" - - -class SmartPointer(object): - """Class to hold a non-managed piece of memory""" - - def __init__(self, raw_pointer, destructor): - self._raw_pointer = raw_pointer - self._destructor = destructor - - def get(self): - return self._raw_pointer - - def release(self): - rp, self._raw_pointer = self._raw_pointer, None - return rp - - def __del__(self): - try: - if self._raw_pointer is not None: - self._destructor(self._raw_pointer) - self._raw_pointer = None - except AttributeError: - pass - - -def load_pycryptodome_raw_lib(name, cdecl): - """Load a shared library and return a handle to it. - - @name, the name of the library expressed as a PyCryptodome module, - for instance Cryptodome.Cipher._raw_cbc. - - @cdecl, the C function declarations. - """ - - split = name.split(".") - dir_comps, basename = split[:-1], split[-1] - attempts = [] - for ext in extension_suffixes: - try: - filename = basename + ext - full_name = pycryptodome_filename(dir_comps, filename) - if not os.path.isfile(full_name): - attempts.append("Not found '%s'" % filename) - continue - return load_lib(full_name, cdecl) - except OSError as exp: - attempts.append("Cannot load '%s': %s" % (filename, str(exp))) - raise OSError("Cannot load native module '%s': %s" % (name, ", ".join(attempts))) - - -def is_buffer(x): - """Return True if object x supports the buffer interface""" - return isinstance(x, (bytes, bytearray, memoryview)) - - -def is_writeable_buffer(x): - return (isinstance(x, bytearray) or - (isinstance(x, memoryview) and not x.readonly)) diff --git a/resources/lib/deps/Cryptodome/Util/_raw_api.pyi b/resources/lib/deps/Cryptodome/Util/_raw_api.pyi deleted file mode 100644 index 2bc5301..0000000 --- a/resources/lib/deps/Cryptodome/Util/_raw_api.pyi +++ /dev/null @@ -1,27 +0,0 @@ -from typing import Any, Optional, Union - -def load_lib(name: str, cdecl: str) -> Any : ... -def c_ulong(x: int ) -> Any : ... -def c_ulonglong(x: int ) -> Any : ... -def c_size_t(x: int) -> Any : ... -def create_string_buffer(init_or_size: Union[bytes,int], size: Optional[int]) -> Any : ... -def get_c_string(c_string: Any) -> bytes : ... -def get_raw_buffer(buf: Any) -> bytes : ... -def c_uint8_ptr(data: Union[bytes, memoryview, bytearray]) -> Any : ... - -class VoidPointer(object): - def get(self) -> Any : ... - def address_of(self) -> Any : ... - -class SmartPointer(object): - def __init__(self, raw_pointer: Any, destructor: Any) -> None : ... - def get(self) -> Any : ... - def release(self) -> Any : ... - -backend : str -null_pointer : Any -ffi: Any - -def load_pycryptodome_raw_lib(name: str, cdecl: str) -> Any : ... -def is_buffer(x: Any) -> bool : ... -def is_writeable_buffer(x: Any) -> bool : ... diff --git a/resources/lib/deps/Cryptodome/Util/_strxor.abi3.so b/resources/lib/deps/Cryptodome/Util/_strxor.abi3.so deleted file mode 100755 index f0f3784dbd744491d6bacecae5604aa045bee150..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20376 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 G*?$7dsyU4S diff --git a/resources/lib/deps/Cryptodome/Util/asn1.py b/resources/lib/deps/Cryptodome/Util/asn1.py deleted file mode 100644 index 36f2d72..0000000 --- a/resources/lib/deps/Cryptodome/Util/asn1.py +++ /dev/null @@ -1,1064 +0,0 @@ -# -*- coding: ascii -*- -# -# Util/asn1.py : Minimal support for ASN.1 DER binary encoding. -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -import struct - -from Cryptodome.Util.py3compat import byte_string, bchr, bord - -from Cryptodome.Util.number import long_to_bytes, bytes_to_long - -__all__ = ['DerObject', 'DerInteger', 'DerBoolean', 'DerOctetString', - 'DerNull', 'DerSequence', 'DerObjectId', 'DerBitString', 'DerSetOf'] - -# Useful references: -# - https://luca.ntop.org/Teaching/Appunti/asn1.html -# - https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/ -# - https://www.zytrax.com/tech/survival/asn1.html -# - https://www.oss.com/asn1/resources/books-whitepapers-pubs/larmouth-asn1-book.pdf -# - https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf -# - https://misc.daniel-marschall.de/asn.1/oid-converter/online.php - -def _is_number(x, only_non_negative=False): - test = 0 - try: - test = x + test - except TypeError: - return False - return not only_non_negative or x >= 0 - - -class BytesIO_EOF(object): - """This class differs from BytesIO in that a ValueError exception is - raised whenever EOF is reached.""" - - def __init__(self, initial_bytes): - self._buffer = initial_bytes - self._index = 0 - self._bookmark = None - - def set_bookmark(self): - self._bookmark = self._index - - def data_since_bookmark(self): - assert self._bookmark is not None - return self._buffer[self._bookmark:self._index] - - def remaining_data(self): - return len(self._buffer) - self._index - - def read(self, length): - new_index = self._index + length - if new_index > len(self._buffer): - raise ValueError("Not enough data for DER decoding: expected %d bytes and found %d" % (new_index, len(self._buffer))) - - result = self._buffer[self._index:new_index] - self._index = new_index - return result - - def read_byte(self): - return bord(self.read(1)[0]) - - -class DerObject(object): - """Base class for defining a single DER object. - - This class should never be directly instantiated. - """ - - def __init__(self, asn1Id=None, payload=b'', implicit=None, - constructed=False, explicit=None): - """Initialize the DER object according to a specific ASN.1 type. - - :Parameters: - asn1Id : integer or byte - The universal DER tag number for this object - (e.g. 0x10 for a SEQUENCE). - If None, the tag is not known yet. - - payload : byte string - The initial payload of the object (that it, - the content octets). - If not specified, the payload is empty. - - implicit : integer or byte - The IMPLICIT tag number (< 0x1F) to use for the encoded object. - It overrides the universal tag *asn1Id*. - It cannot be combined with the ``explicit`` parameter. - By default, there is no IMPLICIT tag. - - constructed : bool - True when the ASN.1 type is *constructed*. - False when it is *primitive* (default). - - explicit : integer or byte - The EXPLICIT tag number (< 0x1F) to use for the encoded object. - It cannot be combined with the ``implicit`` parameter. - By default, there is no EXPLICIT tag. - """ - - if asn1Id is None: - # The tag octet will be read in with ``decode`` - self._tag_octet = None - return - asn1Id = self._convertTag(asn1Id) - - self.payload = payload - - # In a BER/DER identifier octet: - # * bits 4-0 contain the tag value - # * bit 5 is set if the type is 'constructed' - # and unset if 'primitive' - # * bits 7-6 depend on the encoding class - # - # Class | Bit 7, Bit 6 - # ---------------------------------- - # universal | 0 0 - # application | 0 1 - # context-spec | 1 0 (default for IMPLICIT/EXPLICIT) - # private | 1 1 - # - - constructed_bit = 0x20 if constructed else 0x00 - - if None not in (explicit, implicit): - raise ValueError("Explicit and implicit tags are" - " mutually exclusive") - - if implicit is not None: - # IMPLICIT tag overrides asn1Id - self._tag_octet = 0x80 | constructed_bit | self._convertTag(implicit) - elif explicit is not None: - # 'constructed bit' is always asserted for an EXPLICIT tag - self._tag_octet = 0x80 | 0x20 | self._convertTag(explicit) - self._inner_tag_octet = constructed_bit | asn1Id - else: - # Neither IMPLICIT nor EXPLICIT - self._tag_octet = constructed_bit | asn1Id - - def _convertTag(self, tag): - """Check if *tag* is a real DER tag (5 bits). - Convert it from a character to number if necessary. - """ - if not _is_number(tag): - if len(tag) == 1: - tag = bord(tag[0]) - # Ensure that tag is a low tag - if not (_is_number(tag) and 0 <= tag < 0x1F): - raise ValueError("Wrong DER tag") - return tag - - @staticmethod - def _definite_form(length): - """Build length octets according to BER/DER - definite form. - """ - if length > 127: - encoding = long_to_bytes(length) - return bchr(len(encoding) + 128) + encoding - return bchr(length) - - def encode(self): - """Return this DER element, fully encoded as a binary byte string.""" - - # Concatenate identifier octets, length octets, - # and contents octets - - output_payload = self.payload - - # In case of an EXTERNAL tag, first encode the inner - # element. - if hasattr(self, "_inner_tag_octet"): - output_payload = (bchr(self._inner_tag_octet) + - self._definite_form(len(self.payload)) + - self.payload) - - return (bchr(self._tag_octet) + - self._definite_form(len(output_payload)) + - output_payload) - - def _decodeLen(self, s): - """Decode DER length octets from a file.""" - - length = s.read_byte() - - if length > 127: - encoded_length = s.read(length & 0x7F) - if bord(encoded_length[0]) == 0: - raise ValueError("Invalid DER: length has leading zero") - length = bytes_to_long(encoded_length) - if length <= 127: - raise ValueError("Invalid DER: length in long form but smaller than 128") - - return length - - def decode(self, der_encoded, strict=False): - """Decode a complete DER element, and re-initializes this - object with it. - - Args: - der_encoded (byte string): A complete DER element. - - Raises: - ValueError: in case of parsing errors. - """ - - if not byte_string(der_encoded): - raise ValueError("Input is not a byte string") - - s = BytesIO_EOF(der_encoded) - self._decodeFromStream(s, strict) - - # There shouldn't be other bytes left - if s.remaining_data() > 0: - raise ValueError("Unexpected extra data after the DER structure") - - return self - - def _decodeFromStream(self, s, strict): - """Decode a complete DER element from a file.""" - - idOctet = s.read_byte() - if self._tag_octet is not None: - if idOctet != self._tag_octet: - raise ValueError("Unexpected DER tag") - else: - self._tag_octet = idOctet - length = self._decodeLen(s) - self.payload = s.read(length) - - # In case of an EXTERNAL tag, further decode the inner - # element. - if hasattr(self, "_inner_tag_octet"): - p = BytesIO_EOF(self.payload) - inner_octet = p.read_byte() - if inner_octet != self._inner_tag_octet: - raise ValueError("Unexpected internal DER tag") - length = self._decodeLen(p) - self.payload = p.read(length) - - # There shouldn't be other bytes left - if p.remaining_data() > 0: - raise ValueError("Unexpected extra data after the DER structure") - - -class DerInteger(DerObject): - """Class to model a DER INTEGER. - - An example of encoding is:: - - >>> from Cryptodome.Util.asn1 import DerInteger - >>> from binascii import hexlify, unhexlify - >>> int_der = DerInteger(9) - >>> print hexlify(int_der.encode()) - - which will show ``020109``, the DER encoding of 9. - - And for decoding:: - - >>> s = unhexlify(b'020109') - >>> try: - >>> int_der = DerInteger() - >>> int_der.decode(s) - >>> print int_der.value - >>> except ValueError: - >>> print "Not a valid DER INTEGER" - - the output will be ``9``. - - :ivar value: The integer value - :vartype value: integer - """ - - def __init__(self, value=0, implicit=None, explicit=None): - """Initialize the DER object as an INTEGER. - - :Parameters: - value : integer - The value of the integer. - - implicit : integer - The IMPLICIT tag to use for the encoded object. - It overrides the universal tag for INTEGER (2). - """ - - DerObject.__init__(self, 0x02, b'', implicit, - False, explicit) - self.value = value # The integer value - - def encode(self): - """Return the DER INTEGER, fully encoded as a - binary string.""" - - number = self.value - self.payload = b'' - while True: - self.payload = bchr(int(number & 255)) + self.payload - if 128 <= number <= 255: - self.payload = bchr(0x00) + self.payload - if -128 <= number <= 255: - break - number >>= 8 - return DerObject.encode(self) - - def decode(self, der_encoded, strict=False): - """Decode a DER-encoded INTEGER, and re-initializes this - object with it. - - Args: - der_encoded (byte string): A complete INTEGER DER element. - - Raises: - ValueError: in case of parsing errors. - """ - - return DerObject.decode(self, der_encoded, strict=strict) - - def _decodeFromStream(self, s, strict): - """Decode a complete DER INTEGER from a file.""" - - # Fill up self.payload - DerObject._decodeFromStream(self, s, strict) - - if strict: - if len(self.payload) == 0: - raise ValueError("Invalid encoding for DER INTEGER: empty payload") - if len(self.payload) >= 2 and struct.unpack('>H', self.payload[:2])[0] < 0x80: - raise ValueError("Invalid encoding for DER INTEGER: leading zero") - - # Derive self.value from self.payload - self.value = 0 - bits = 1 - for i in self.payload: - self.value *= 256 - self.value += bord(i) - bits <<= 8 - if self.payload and bord(self.payload[0]) & 0x80: - self.value -= bits - - -class DerBoolean(DerObject): - """Class to model a DER-encoded BOOLEAN. - - An example of encoding is:: - - >>> from Cryptodome.Util.asn1 import DerBoolean - >>> bool_der = DerBoolean(True) - >>> print(bool_der.encode().hex()) - - which will show ``0101ff``, the DER encoding of True. - - And for decoding:: - - >>> s = bytes.fromhex('0101ff') - >>> try: - >>> bool_der = DerBoolean() - >>> bool_der.decode(s) - >>> print(bool_der.value) - >>> except ValueError: - >>> print "Not a valid DER BOOLEAN" - - the output will be ``True``. - - :ivar value: The boolean value - :vartype value: boolean - """ - def __init__(self, value=False, implicit=None, explicit=None): - """Initialize the DER object as a BOOLEAN. - - Args: - value (boolean): - The value of the boolean. Default is False. - - implicit (integer or byte): - The IMPLICIT tag number (< 0x1F) to use for the encoded object. - It overrides the universal tag for BOOLEAN (1). - It cannot be combined with the ``explicit`` parameter. - By default, there is no IMPLICIT tag. - - explicit (integer or byte): - The EXPLICIT tag number (< 0x1F) to use for the encoded object. - It cannot be combined with the ``implicit`` parameter. - By default, there is no EXPLICIT tag. - """ - - DerObject.__init__(self, 0x01, b'', implicit, False, explicit) - self.value = value # The boolean value - - def encode(self): - """Return the DER BOOLEAN, fully encoded as a binary string.""" - - self.payload = b'\xFF' if self.value else b'\x00' - return DerObject.encode(self) - - def decode(self, der_encoded, strict=False): - """Decode a DER-encoded BOOLEAN, and re-initializes this object with it. - - Args: - der_encoded (byte string): A DER-encoded BOOLEAN. - - Raises: - ValueError: in case of parsing errors. - """ - - return DerObject.decode(self, der_encoded, strict) - - def _decodeFromStream(self, s, strict): - """Decode a DER-encoded BOOLEAN from a file.""" - - # Fill up self.payload - DerObject._decodeFromStream(self, s, strict) - - if len(self.payload) != 1: - raise ValueError("Invalid encoding for DER BOOLEAN: payload is not 1 byte") - - if bord(self.payload[0]) == 0: - self.value = False - elif bord(self.payload[0]) == 0xFF: - self.value = True - else: - raise ValueError("Invalid payload for DER BOOLEAN") - - -class DerSequence(DerObject): - """Class to model a DER SEQUENCE. - - This object behaves like a dynamic Python sequence. - - Sub-elements that are INTEGERs behave like Python integers. - - Any other sub-element is a binary string encoded as a complete DER - sub-element (TLV). - - An example of encoding is: - - >>> from Cryptodome.Util.asn1 import DerSequence, DerInteger - >>> from binascii import hexlify, unhexlify - >>> obj_der = unhexlify('070102') - >>> seq_der = DerSequence([4]) - >>> seq_der.append(9) - >>> seq_der.append(obj_der.encode()) - >>> print hexlify(seq_der.encode()) - - which will show ``3009020104020109070102``, the DER encoding of the - sequence containing ``4``, ``9``, and the object with payload ``02``. - - For decoding: - - >>> s = unhexlify(b'3009020104020109070102') - >>> try: - >>> seq_der = DerSequence() - >>> seq_der.decode(s) - >>> print len(seq_der) - >>> print seq_der[0] - >>> print seq_der[:] - >>> except ValueError: - >>> print "Not a valid DER SEQUENCE" - - the output will be:: - - 3 - 4 - [4, 9, b'\x07\x01\x02'] - - """ - - def __init__(self, startSeq=None, implicit=None, explicit=None): - """Initialize the DER object as a SEQUENCE. - - :Parameters: - startSeq : Python sequence - A sequence whose element are either integers or - other DER objects. - - implicit : integer or byte - The IMPLICIT tag number (< 0x1F) to use for the encoded object. - It overrides the universal tag for SEQUENCE (16). - It cannot be combined with the ``explicit`` parameter. - By default, there is no IMPLICIT tag. - - explicit : integer or byte - The EXPLICIT tag number (< 0x1F) to use for the encoded object. - It cannot be combined with the ``implicit`` parameter. - By default, there is no EXPLICIT tag. - """ - - DerObject.__init__(self, 0x10, b'', implicit, True, explicit) - if startSeq is None: - self._seq = [] - else: - self._seq = startSeq - - # A few methods to make it behave like a python sequence - - def __delitem__(self, n): - del self._seq[n] - - def __getitem__(self, n): - return self._seq[n] - - def __setitem__(self, key, value): - self._seq[key] = value - - def __setslice__(self, i, j, sequence): - self._seq[i:j] = sequence - - def __delslice__(self, i, j): - del self._seq[i:j] - - def __getslice__(self, i, j): - return self._seq[max(0, i):max(0, j)] - - def __len__(self): - return len(self._seq) - - def __iadd__(self, item): - self._seq.append(item) - return self - - def append(self, item): - self._seq.append(item) - return self - - def insert(self, index, item): - self._seq.insert(index, item) - return self - - def hasInts(self, only_non_negative=True): - """Return the number of items in this sequence that are - integers. - - Args: - only_non_negative (boolean): - If ``True``, negative integers are not counted in. - """ - - items = [x for x in self._seq if _is_number(x, only_non_negative)] - return len(items) - - def hasOnlyInts(self, only_non_negative=True): - """Return ``True`` if all items in this sequence are integers - or non-negative integers. - - This function returns False is the sequence is empty, - or at least one member is not an integer. - - Args: - only_non_negative (boolean): - If ``True``, the presence of negative integers - causes the method to return ``False``.""" - return self._seq and self.hasInts(only_non_negative) == len(self._seq) - - def encode(self): - """Return this DER SEQUENCE, fully encoded as a - binary string. - - Raises: - ValueError: if some elements in the sequence are neither integers - nor byte strings. - """ - self.payload = b'' - for item in self._seq: - if byte_string(item): - self.payload += item - elif _is_number(item): - self.payload += DerInteger(item).encode() - else: - self.payload += item.encode() - return DerObject.encode(self) - - def decode(self, der_encoded, strict=False, nr_elements=None, only_ints_expected=False): - """Decode a complete DER SEQUENCE, and re-initializes this - object with it. - - Args: - der_encoded (byte string): - A complete SEQUENCE DER element. - nr_elements (None or integer or list of integers): - The number of members the SEQUENCE can have - only_ints_expected (boolean): - Whether the SEQUENCE is expected to contain only integers. - strict (boolean): - Whether decoding must check for strict DER compliancy. - - Raises: - ValueError: in case of parsing errors. - - DER INTEGERs are decoded into Python integers. Any other DER - element is not decoded. Its validity is not checked. - """ - - self._nr_elements = nr_elements - result = DerObject.decode(self, der_encoded, strict=strict) - - if only_ints_expected and not self.hasOnlyInts(): - raise ValueError("Some members are not INTEGERs") - - return result - - def _decodeFromStream(self, s, strict): - """Decode a complete DER SEQUENCE from a file.""" - - self._seq = [] - - # Fill up self.payload - DerObject._decodeFromStream(self, s, strict) - - # Add one item at a time to self.seq, by scanning self.payload - p = BytesIO_EOF(self.payload) - while p.remaining_data() > 0: - p.set_bookmark() - - der = DerObject() - der._decodeFromStream(p, strict) - - # Parse INTEGERs differently - if der._tag_octet != 0x02: - self._seq.append(p.data_since_bookmark()) - else: - derInt = DerInteger() - data = p.data_since_bookmark() - derInt.decode(data, strict=strict) - self._seq.append(derInt.value) - - ok = True - if self._nr_elements is not None: - try: - ok = len(self._seq) in self._nr_elements - except TypeError: - ok = len(self._seq) == self._nr_elements - - if not ok: - raise ValueError("Unexpected number of members (%d)" - " in the sequence" % len(self._seq)) - - -class DerOctetString(DerObject): - """Class to model a DER OCTET STRING. - - An example of encoding is: - - >>> from Cryptodome.Util.asn1 import DerOctetString - >>> from binascii import hexlify, unhexlify - >>> os_der = DerOctetString(b'\\xaa') - >>> os_der.payload += b'\\xbb' - >>> print hexlify(os_der.encode()) - - which will show ``0402aabb``, the DER encoding for the byte string - ``b'\\xAA\\xBB'``. - - For decoding: - - >>> s = unhexlify(b'0402aabb') - >>> try: - >>> os_der = DerOctetString() - >>> os_der.decode(s) - >>> print hexlify(os_der.payload) - >>> except ValueError: - >>> print "Not a valid DER OCTET STRING" - - the output will be ``aabb``. - - :ivar payload: The content of the string - :vartype payload: byte string - """ - - def __init__(self, value=b'', implicit=None): - """Initialize the DER object as an OCTET STRING. - - :Parameters: - value : byte string - The initial payload of the object. - If not specified, the payload is empty. - - implicit : integer - The IMPLICIT tag to use for the encoded object. - It overrides the universal tag for OCTET STRING (4). - """ - DerObject.__init__(self, 0x04, value, implicit, False) - - -class DerNull(DerObject): - """Class to model a DER NULL element.""" - - def __init__(self): - """Initialize the DER object as a NULL.""" - - DerObject.__init__(self, 0x05, b'', None, False) - - -class DerObjectId(DerObject): - """Class to model a DER OBJECT ID. - - An example of encoding is: - - >>> from Cryptodome.Util.asn1 import DerObjectId - >>> from binascii import hexlify, unhexlify - >>> oid_der = DerObjectId("1.2") - >>> oid_der.value += ".840.113549.1.1.1" - >>> print hexlify(oid_der.encode()) - - which will show ``06092a864886f70d010101``, the DER encoding for the - RSA Object Identifier ``1.2.840.113549.1.1.1``. - - For decoding: - - >>> s = unhexlify(b'06092a864886f70d010101') - >>> try: - >>> oid_der = DerObjectId() - >>> oid_der.decode(s) - >>> print oid_der.value - >>> except ValueError: - >>> print "Not a valid DER OBJECT ID" - - the output will be ``1.2.840.113549.1.1.1``. - - :ivar value: The Object ID (OID), a dot separated list of integers - :vartype value: string - """ - - def __init__(self, value='', implicit=None, explicit=None): - """Initialize the DER object as an OBJECT ID. - - :Parameters: - value : string - The initial Object Identifier (e.g. "1.2.0.0.6.2"). - implicit : integer - The IMPLICIT tag to use for the encoded object. - It overrides the universal tag for OBJECT ID (6). - explicit : integer - The EXPLICIT tag to use for the encoded object. - """ - DerObject.__init__(self, 0x06, b'', implicit, False, explicit) - self.value = value - - def encode(self): - """Return the DER OBJECT ID, fully encoded as a - binary string.""" - - comps = [int(x) for x in self.value.split(".")] - - if len(comps) < 2: - raise ValueError("Not a valid Object Identifier string") - if comps[0] > 2: - raise ValueError("First component must be 0, 1 or 2") - if comps[0] < 2 and comps[1] > 39: - raise ValueError("Second component must be 39 at most") - - subcomps = [40 * comps[0] + comps[1]] + comps[2:] - - encoding = [] - for v in reversed(subcomps): - encoding.append(v & 0x7F) - v >>= 7 - while v: - encoding.append((v & 0x7F) | 0x80) - v >>= 7 - - self.payload = b''.join([bchr(x) for x in reversed(encoding)]) - return DerObject.encode(self) - - def decode(self, der_encoded, strict=False): - """Decode a complete DER OBJECT ID, and re-initializes this - object with it. - - Args: - der_encoded (byte string): - A complete DER OBJECT ID. - strict (boolean): - Whether decoding must check for strict DER compliancy. - - Raises: - ValueError: in case of parsing errors. - """ - - return DerObject.decode(self, der_encoded, strict) - - def _decodeFromStream(self, s, strict): - """Decode a complete DER OBJECT ID from a file.""" - - # Fill up self.payload - DerObject._decodeFromStream(self, s, strict) - - # Derive self.value from self.payload - p = BytesIO_EOF(self.payload) - - subcomps = [] - v = 0 - while p.remaining_data(): - c = p.read_byte() - v = (v << 7) + (c & 0x7F) - if not (c & 0x80): - subcomps.append(v) - v = 0 - - if len(subcomps) == 0: - raise ValueError("Empty payload") - - if subcomps[0] < 40: - subcomps[:1] = [0, subcomps[0]] - elif subcomps[0] < 80: - subcomps[:1] = [1, subcomps[0] - 40] - else: - subcomps[:1] = [2, subcomps[0] - 80] - - self.value = ".".join([str(x) for x in subcomps]) - - -class DerBitString(DerObject): - """Class to model a DER BIT STRING. - - An example of encoding is: - - >>> from Cryptodome.Util.asn1 import DerBitString - >>> bs_der = DerBitString(b'\\xAA') - >>> bs_der.value += b'\\xBB' - >>> print(bs_der.encode().hex()) - - which will show ``030300aabb``, the DER encoding for the bit string - ``b'\\xAA\\xBB'``. - - For decoding: - - >>> s = bytes.fromhex('030300aabb') - >>> try: - >>> bs_der = DerBitString() - >>> bs_der.decode(s) - >>> print(bs_der.value.hex()) - >>> except ValueError: - >>> print "Not a valid DER BIT STRING" - - the output will be ``aabb``. - - :ivar value: The content of the string - :vartype value: byte string - """ - - def __init__(self, value=b'', implicit=None, explicit=None): - """Initialize the DER object as a BIT STRING. - - :Parameters: - value : byte string or DER object - The initial, packed bit string. - If not specified, the bit string is empty. - implicit : integer - The IMPLICIT tag to use for the encoded object. - It overrides the universal tag for BIT STRING (3). - explicit : integer - The EXPLICIT tag to use for the encoded object. - """ - DerObject.__init__(self, 0x03, b'', implicit, False, explicit) - - # The bitstring value (packed) - if isinstance(value, DerObject): - self.value = value.encode() - else: - self.value = value - - def encode(self): - """Return the DER BIT STRING, fully encoded as a - byte string.""" - - # Add padding count byte - self.payload = b'\x00' + self.value - return DerObject.encode(self) - - def decode(self, der_encoded, strict=False): - """Decode a complete DER BIT STRING, and re-initializes this - object with it. - - Args: - der_encoded (byte string): a complete DER BIT STRING. - strict (boolean): - Whether decoding must check for strict DER compliancy. - - Raises: - ValueError: in case of parsing errors. - """ - - return DerObject.decode(self, der_encoded, strict) - - def _decodeFromStream(self, s, strict): - """Decode a complete DER BIT STRING DER from a file.""" - - # Fill-up self.payload - DerObject._decodeFromStream(self, s, strict) - - if self.payload and bord(self.payload[0]) != 0: - raise ValueError("Not a valid BIT STRING") - - # Fill-up self.value - self.value = b'' - # Remove padding count byte - if self.payload: - self.value = self.payload[1:] - - -class DerSetOf(DerObject): - """Class to model a DER SET OF. - - An example of encoding is: - - >>> from Cryptodome.Util.asn1 import DerBitString - >>> from binascii import hexlify, unhexlify - >>> so_der = DerSetOf([4,5]) - >>> so_der.add(6) - >>> print hexlify(so_der.encode()) - - which will show ``3109020104020105020106``, the DER encoding - of a SET OF with items 4,5, and 6. - - For decoding: - - >>> s = unhexlify(b'3109020104020105020106') - >>> try: - >>> so_der = DerSetOf() - >>> so_der.decode(s) - >>> print [x for x in so_der] - >>> except ValueError: - >>> print "Not a valid DER SET OF" - - the output will be ``[4, 5, 6]``. - """ - - def __init__(self, startSet=None, implicit=None): - """Initialize the DER object as a SET OF. - - :Parameters: - startSet : container - The initial set of integers or DER encoded objects. - implicit : integer - The IMPLICIT tag to use for the encoded object. - It overrides the universal tag for SET OF (17). - """ - DerObject.__init__(self, 0x11, b'', implicit, True) - self._seq = [] - - # All elements must be of the same type (and therefore have the - # same leading octet) - self._elemOctet = None - - if startSet: - for e in startSet: - self.add(e) - - def __getitem__(self, n): - return self._seq[n] - - def __iter__(self): - return iter(self._seq) - - def __len__(self): - return len(self._seq) - - def add(self, elem): - """Add an element to the set. - - Args: - elem (byte string or integer): - An element of the same type of objects already in the set. - It can be an integer or a DER encoded object. - """ - - if _is_number(elem): - eo = 0x02 - elif isinstance(elem, DerObject): - eo = self._tag_octet - else: - eo = bord(elem[0]) - - if self._elemOctet != eo: - if self._elemOctet is not None: - raise ValueError("New element does not belong to the set") - self._elemOctet = eo - - if elem not in self._seq: - self._seq.append(elem) - - def decode(self, der_encoded, strict=False): - """Decode a complete SET OF DER element, and re-initializes this - object with it. - - DER INTEGERs are decoded into Python integers. Any other DER - element is left undecoded; its validity is not checked. - - Args: - der_encoded (byte string): a complete DER BIT SET OF. - strict (boolean): - Whether decoding must check for strict DER compliancy. - - Raises: - ValueError: in case of parsing errors. - """ - - return DerObject.decode(self, der_encoded, strict) - - def _decodeFromStream(self, s, strict): - """Decode a complete DER SET OF from a file.""" - - self._seq = [] - - # Fill up self.payload - DerObject._decodeFromStream(self, s, strict) - - # Add one item at a time to self.seq, by scanning self.payload - p = BytesIO_EOF(self.payload) - setIdOctet = -1 - while p.remaining_data() > 0: - p.set_bookmark() - - der = DerObject() - der._decodeFromStream(p, strict) - - # Verify that all members are of the same type - if setIdOctet < 0: - setIdOctet = der._tag_octet - else: - if setIdOctet != der._tag_octet: - raise ValueError("Not all elements are of the same DER type") - - # Parse INTEGERs differently - if setIdOctet != 0x02: - self._seq.append(p.data_since_bookmark()) - else: - derInt = DerInteger() - derInt.decode(p.data_since_bookmark(), strict) - self._seq.append(derInt.value) - # end - - def encode(self): - """Return this SET OF DER element, fully encoded as a - binary string. - """ - - # Elements in the set must be ordered in lexicographic order - ordered = [] - for item in self._seq: - if _is_number(item): - bys = DerInteger(item).encode() - elif isinstance(item, DerObject): - bys = item.encode() - else: - bys = item - ordered.append(bys) - ordered.sort() - self.payload = b''.join(ordered) - return DerObject.encode(self) diff --git a/resources/lib/deps/Cryptodome/Util/asn1.pyi b/resources/lib/deps/Cryptodome/Util/asn1.pyi deleted file mode 100644 index ee4891c..0000000 --- a/resources/lib/deps/Cryptodome/Util/asn1.pyi +++ /dev/null @@ -1,80 +0,0 @@ -from typing import Optional, Sequence, Union, Set, Iterable - -__all__ = ['DerObject', 'DerInteger', 'DerOctetString', 'DerNull', - 'DerSequence', 'DerObjectId', 'DerBitString', 'DerSetOf'] - -# TODO: Make the encoded DerObjects their own type, so that DerSequence and -# DerSetOf can check their contents better - -class BytesIO_EOF: - def __init__(self, initial_bytes: bytes) -> None: ... - def set_bookmark(self) -> None: ... - def data_since_bookmark(self) -> bytes: ... - def remaining_data(self) -> int: ... - def read(self, length: int) -> bytes: ... - def read_byte(self) -> bytes: ... - -class DerObject: - payload: bytes - def __init__(self, asn1Id: Optional[int]=None, payload: Optional[bytes]=..., implicit: Optional[int]=None, - constructed: Optional[bool]=False, explicit: Optional[int]=None) -> None: ... - def encode(self) -> bytes: ... - def decode(self, der_encoded: bytes, strict: bool=...) -> DerObject: ... - -class DerInteger(DerObject): - value: int - def __init__(self, value: Optional[int]= 0, implicit: Optional[int]=None, explicit: Optional[int]=None) -> None: ... - def encode(self) -> bytes: ... - def decode(self, der_encoded: bytes, strict: bool=...) -> DerInteger: ... - -class DerBoolean(DerObject): - value: bool - def __init__(self, value: bool=..., implicit: Optional[Union[int, bytes]]=..., explicit: Optional[Union[int, bytes]]=...) -> None: ... - def encode(self) -> bytes: ... - def decode(self, der_encoded: bytes, strict: bool=...) -> DerBoolean: ... - -class DerSequence(DerObject): - def __init__(self, startSeq: Optional[Sequence[Union[int, DerInteger, DerObject]]]=None, implicit: Optional[int]=None) -> None: ... - def __delitem__(self, n: int) -> None: ... - def __getitem__(self, n: int) -> None: ... - def __setitem__(self, key: int, value: DerObject) -> None: ... - def __setslice__(self, i: int, j: int, sequence: Sequence) -> None: ... - def __delslice__(self, i: int, j: int) -> None: ... - def __getslice__(self, i: int, j: int) -> DerSequence: ... - def __len__(self) -> int: ... - def __iadd__(self, item: DerObject) -> DerSequence: ... - def append(self, item: DerObject) -> DerSequence: ... - def hasInts(self, only_non_negative: Optional[bool]=True) -> int: ... - def hasOnlyInts(self, only_non_negative: Optional[bool]=True) -> bool: ... - def encode(self) -> bytes: ... - def decode(self, der_encoded: bytes, strict: bool=..., nr_elements: Optional[int]=None, only_ints_expected: Optional[bool]=False) -> DerSequence: ... - -class DerOctetString(DerObject): - payload: bytes - def __init__(self, value: Optional[bytes]=..., implicit: Optional[int]=None) -> None: ... - -class DerNull(DerObject): - def __init__(self) -> None: ... - -class DerObjectId(DerObject): - value: str - def __init__(self, value: Optional[str]=..., implicit: Optional[int]=None, explicit: Optional[int]=None) -> None: ... - def encode(self) -> bytes: ... - def decode(self, der_encoded: bytes, strict: bool=...) -> DerObjectId: ... - -class DerBitString(DerObject): - value: bytes - def __init__(self, value: Optional[bytes]=..., implicit: Optional[int]=None, explicit: Optional[int]=None) -> None: ... - def encode(self) -> bytes: ... - def decode(self, der_encoded: bytes, strict: bool=...) -> DerBitString: ... - -DerSetElement = Union[bytes, int] - -class DerSetOf(DerObject): - def __init__(self, startSet: Optional[Set[DerSetElement]]=None, implicit: Optional[int]=None) -> None: ... - def __getitem__(self, n: int) -> DerSetElement: ... - def __iter__(self) -> Iterable: ... - def __len__(self) -> int: ... - def add(self, elem: DerSetElement) -> None: ... - def decode(self, der_encoded: bytes, strict: bool=...) -> DerObject: ... - def encode(self) -> bytes: ... diff --git a/resources/lib/deps/Cryptodome/Util/number.py b/resources/lib/deps/Cryptodome/Util/number.py deleted file mode 100644 index 6d59fd9..0000000 --- a/resources/lib/deps/Cryptodome/Util/number.py +++ /dev/null @@ -1,1525 +0,0 @@ -# -# number.py : Number-theoretic functions -# -# Part of the Python Cryptography Toolkit -# -# Written by Andrew M. Kuchling, Barry A. Warsaw, and others -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== -# - -import math -import sys -import struct -from Cryptodome import Random -from Cryptodome.Util.py3compat import iter_range - -# Backward compatibility -_fastmath = None - - -def ceil_div(n, d): - """Return ceil(n/d), that is, the smallest integer r such that r*d >= n""" - - if d == 0: - raise ZeroDivisionError() - if (n < 0) or (d < 0): - raise ValueError("Non positive values") - r, q = divmod(n, d) - if (n != 0) and (q != 0): - r += 1 - return r - - -def size (N): - """Returns the size of the number N in bits.""" - - if N < 0: - raise ValueError("Size in bits only available for non-negative numbers") - return N.bit_length() - - -def getRandomInteger(N, randfunc=None): - """Return a random number at most N bits long. - - If :data:`randfunc` is omitted, then :meth:`Random.get_random_bytes` is used. - - .. deprecated:: 3.0 - This function is for internal use only and may be renamed or removed in - the future. Use :func:`Cryptodome.Random.random.getrandbits` instead. - """ - - if randfunc is None: - randfunc = Random.get_random_bytes - - S = randfunc(N>>3) - odd_bits = N % 8 - if odd_bits != 0: - rand_bits = ord(randfunc(1)) >> (8-odd_bits) - S = struct.pack('B', rand_bits) + S - value = bytes_to_long(S) - return value - -def getRandomRange(a, b, randfunc=None): - """Return a random number *n* so that *a <= n < b*. - - If :data:`randfunc` is omitted, then :meth:`Random.get_random_bytes` is used. - - .. deprecated:: 3.0 - This function is for internal use only and may be renamed or removed in - the future. Use :func:`Cryptodome.Random.random.randrange` instead. - """ - - range_ = b - a - 1 - bits = size(range_) - value = getRandomInteger(bits, randfunc) - while value > range_: - value = getRandomInteger(bits, randfunc) - return a + value - -def getRandomNBitInteger(N, randfunc=None): - """Return a random number with exactly N-bits, - i.e. a random number between 2**(N-1) and (2**N)-1. - - If :data:`randfunc` is omitted, then :meth:`Random.get_random_bytes` is used. - - .. deprecated:: 3.0 - This function is for internal use only and may be renamed or removed in - the future. - """ - - value = getRandomInteger (N-1, randfunc) - value |= 2 ** (N-1) # Ensure high bit is set - assert size(value) >= N - return value - - -if sys.version_info[:2] >= (3, 5): - - GCD = math.gcd - -else: - - def GCD(x,y): - """Greatest Common Denominator of :data:`x` and :data:`y`. - """ - - x = abs(x) ; y = abs(y) - while x > 0: - x, y = y % x, x - return y - - -if sys.version_info[:2] >= (3, 8): - - def inverse(u, v): - """The inverse of :data:`u` *mod* :data:`v`.""" - - if v == 0: - raise ZeroDivisionError("Modulus cannot be zero") - if v < 0: - raise ValueError("Modulus cannot be negative") - - return pow(u, -1, v) - -else: - - def inverse(u, v): - """The inverse of :data:`u` *mod* :data:`v`.""" - - if v == 0: - raise ZeroDivisionError("Modulus cannot be zero") - if v < 0: - raise ValueError("Modulus cannot be negative") - - u3, v3 = u, v - u1, v1 = 1, 0 - while v3 > 0: - q = u3 // v3 - u1, v1 = v1, u1 - v1*q - u3, v3 = v3, u3 - v3*q - if u3 != 1: - raise ValueError("No inverse value can be computed") - while u1<0: - u1 = u1 + v - return u1 - -# Given a number of bits to generate and a random generation function, -# find a prime number of the appropriate size. - -def getPrime(N, randfunc=None): - """Return a random N-bit prime number. - - N must be an integer larger than 1. - If randfunc is omitted, then :meth:`Random.get_random_bytes` is used. - """ - if randfunc is None: - randfunc = Random.get_random_bytes - - if N < 2: - raise ValueError("N must be larger than 1") - - while True: - number = getRandomNBitInteger(N, randfunc) | 1 - if isPrime(number, randfunc=randfunc): - break - return number - - -def _rabinMillerTest(n, rounds, randfunc=None): - """_rabinMillerTest(n:long, rounds:int, randfunc:callable):int - Tests if n is prime. - Returns 0 when n is definitely composite. - Returns 1 when n is probably prime. - Returns 2 when n is definitely prime. - - If randfunc is omitted, then Random.new().read is used. - - This function is for internal use only and may be renamed or removed in - the future. - """ - # check special cases (n==2, n even, n < 2) - if n < 3 or (n & 1) == 0: - return n == 2 - # n might be very large so it might be beneficial to precalculate n-1 - n_1 = n - 1 - # determine m and b so that 2**b * m = n - 1 and b maximal - b = 0 - m = n_1 - while (m & 1) == 0: - b += 1 - m >>= 1 - - tested = [] - # we need to do at most n-2 rounds. - for i in iter_range (min (rounds, n-2)): - # randomly choose a < n and make sure it hasn't been tested yet - a = getRandomRange (2, n, randfunc) - while a in tested: - a = getRandomRange (2, n, randfunc) - tested.append (a) - # do the rabin-miller test - z = pow (a, m, n) # (a**m) % n - if z == 1 or z == n_1: - continue - composite = 1 - for r in iter_range(b): - z = (z * z) % n - if z == 1: - return 0 - elif z == n_1: - composite = 0 - break - if composite: - return 0 - return 1 - -def getStrongPrime(N, e=0, false_positive_prob=1e-6, randfunc=None): - r""" - Return a random strong *N*-bit prime number. - In this context, *p* is a strong prime if *p-1* and *p+1* have at - least one large prime factor. - - Args: - N (integer): the exact length of the strong prime. - It must be a multiple of 128 and > 512. - e (integer): if provided, the returned prime (minus 1) - will be coprime to *e* and thus suitable for RSA where - *e* is the public exponent. - false_positive_prob (float): - The statistical probability for the result not to be actually a - prime. It defaults to 10\ :sup:`-6`. - Note that the real probability of a false-positive is far less. This is - just the mathematically provable limit. - randfunc (callable): - A function that takes a parameter *N* and that returns - a random byte string of such length. - If omitted, :func:`Cryptodome.Random.get_random_bytes` is used. - Return: - The new strong prime. - - .. deprecated:: 3.0 - This function is for internal use only and may be renamed or removed in - the future. - """ - - # This function was implemented following the - # instructions found in the paper: - # "FAST GENERATION OF RANDOM, STRONG RSA PRIMES" - # by Robert D. Silverman - # RSA Laboratories - # May 17, 1997 - # which by the time of writing could be freely downloaded here: - # http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.17.2713&rep=rep1&type=pdf - - if randfunc is None: - randfunc = Random.get_random_bytes - - # Use the accelerator if available - if _fastmath is not None: - return _fastmath.getStrongPrime(long(N), long(e), false_positive_prob, - randfunc) - - if (N < 512) or ((N % 128) != 0): - raise ValueError ("bits must be multiple of 128 and > 512") - - rabin_miller_rounds = int(math.ceil(-math.log(false_positive_prob)/math.log(4))) - - # calculate range for X - # lower_bound = sqrt(2) * 2^{511 + 128*x} - # upper_bound = 2^{512 + 128*x} - 1 - x = (N - 512) >> 7 - # We need to approximate the sqrt(2) in the lower_bound by an integer - # expression because floating point math overflows with these numbers - lower_bound = (14142135623730950489 * (2 ** (511 + 128*x))) // 10000000000000000000 - upper_bound = (1 << (512 + 128*x)) - 1 - # Randomly choose X in calculated range - X = getRandomRange (lower_bound, upper_bound, randfunc) - - # generate p1 and p2 - p = [0, 0] - for i in (0, 1): - # randomly choose 101-bit y - y = getRandomNBitInteger (101, randfunc) - # initialize the field for sieving - field = [0] * 5 * len (sieve_base) - # sieve the field - for prime in sieve_base: - offset = y % prime - for j in iter_range((prime - offset) % prime, len (field), prime): - field[j] = 1 - - # look for suitable p[i] starting at y - result = 0 - for j in range(len(field)): - composite = field[j] - # look for next canidate - if composite: - continue - tmp = y + j - result = _rabinMillerTest (tmp, rabin_miller_rounds) - if result > 0: - p[i] = tmp - break - if result == 0: - raise RuntimeError ("Couln't find prime in field. " - "Developer: Increase field_size") - - # Calculate R - # R = (p2^{-1} mod p1) * p2 - (p1^{-1} mod p2) * p1 - tmp1 = inverse (p[1], p[0]) * p[1] # (p2^-1 mod p1)*p2 - tmp2 = inverse (p[0], p[1]) * p[0] # (p1^-1 mod p2)*p1 - R = tmp1 - tmp2 # (p2^-1 mod p1)*p2 - (p1^-1 mod p2)*p1 - - # search for final prime number starting by Y0 - # Y0 = X + (R - X mod p1p2) - increment = p[0] * p[1] - X = X + (R - (X % increment)) - while 1: - is_possible_prime = 1 - # first check candidate against sieve_base - for prime in sieve_base: - if (X % prime) == 0: - is_possible_prime = 0 - break - # if e is given make sure that e and X-1 are coprime - # this is not necessarily a strong prime criterion but useful when - # creating them for RSA where the p-1 and q-1 should be coprime to - # the public exponent e - if e and is_possible_prime: - if e & 1: - if GCD(e, X-1) != 1: - is_possible_prime = 0 - else: - if GCD(e, (X-1) // 2) != 1: - is_possible_prime = 0 - - # do some Rabin-Miller-Tests - if is_possible_prime: - result = _rabinMillerTest (X, rabin_miller_rounds) - if result > 0: - break - X += increment - # abort when X has more bits than requested - # TODO: maybe we shouldn't abort but rather start over. - if X >= 1 << N: - raise RuntimeError ("Couln't find prime in field. " - "Developer: Increase field_size") - return X - -def isPrime(N, false_positive_prob=1e-6, randfunc=None): - r"""Test if a number *N* is a prime. - - Args: - false_positive_prob (float): - The statistical probability for the result not to be actually a - prime. It defaults to 10\ :sup:`-6`. - Note that the real probability of a false-positive is far less. - This is just the mathematically provable limit. - randfunc (callable): - A function that takes a parameter *N* and that returns - a random byte string of such length. - If omitted, :func:`Cryptodome.Random.get_random_bytes` is used. - - Return: - `True` is the input is indeed prime. - """ - - if randfunc is None: - randfunc = Random.get_random_bytes - - if _fastmath is not None: - return _fastmath.isPrime(long(N), false_positive_prob, randfunc) - - if N < 3 or N & 1 == 0: - return N == 2 - for p in sieve_base: - if N == p: - return True - if N % p == 0: - return False - - rounds = int(math.ceil(-math.log(false_positive_prob)/math.log(4))) - return bool(_rabinMillerTest(N, rounds, randfunc)) - - -# Improved conversion functions contributed by Barry Warsaw, after -# careful benchmarking - -import struct - -def long_to_bytes(n, blocksize=0): - """Convert a positive integer to a byte string using big endian encoding. - - If :data:`blocksize` is absent or zero, the byte string will - be of minimal length. - - Otherwise, the length of the byte string is guaranteed to be a multiple - of :data:`blocksize`. If necessary, zeroes (``\\x00``) are added at the left. - - .. note:: - In Python 3, if you are sure that :data:`n` can fit into - :data:`blocksize` bytes, you can simply use the native method instead:: - - >>> n.to_bytes(blocksize, 'big') - - For instance:: - - >>> n = 80 - >>> n.to_bytes(2, 'big') - b'\\x00P' - - However, and unlike this ``long_to_bytes()`` function, - an ``OverflowError`` exception is raised if :data:`n` does not fit. - """ - - if n < 0 or blocksize < 0: - raise ValueError("Values must be non-negative") - - result = [] - pack = struct.pack - - # Fill the first block independently from the value of n - bsr = blocksize - while bsr >= 8: - result.insert(0, pack('>Q', n & 0xFFFFFFFFFFFFFFFF)) - n = n >> 64 - bsr -= 8 - - while bsr >= 4: - result.insert(0, pack('>I', n & 0xFFFFFFFF)) - n = n >> 32 - bsr -= 4 - - while bsr > 0: - result.insert(0, pack('>B', n & 0xFF)) - n = n >> 8 - bsr -= 1 - - if n == 0: - if len(result) == 0: - bresult = b'\x00' - else: - bresult = b''.join(result) - else: - # The encoded number exceeds the block size - while n > 0: - result.insert(0, pack('>Q', n & 0xFFFFFFFFFFFFFFFF)) - n = n >> 64 - result[0] = result[0].lstrip(b'\x00') - bresult = b''.join(result) - # bresult has minimum length here - if blocksize > 0: - target_len = ((len(bresult) - 1) // blocksize + 1) * blocksize - bresult = b'\x00' * (target_len - len(bresult)) + bresult - - return bresult - - -def bytes_to_long(s): - """Convert a byte string to a long integer (big endian). - - In Python 3.2+, use the native method instead:: - - >>> int.from_bytes(s, 'big') - - For instance:: - - >>> int.from_bytes(b'\x00P', 'big') - 80 - - This is (essentially) the inverse of :func:`long_to_bytes`. - """ - acc = 0 - - unpack = struct.unpack - - # Up to Python 2.7.4, struct.unpack can't work with bytearrays nor - # memoryviews - if sys.version_info[0:3] < (2, 7, 4): - if isinstance(s, bytearray): - s = bytes(s) - elif isinstance(s, memoryview): - s = s.tobytes() - - length = len(s) - if length % 4: - extra = (4 - length % 4) - s = b'\x00' * extra + s - length = length + extra - for i in range(0, length, 4): - acc = (acc << 32) + unpack('>I', s[i:i+4])[0] - return acc - - -# For backwards compatibility... -import warnings -def long2str(n, blocksize=0): - warnings.warn("long2str() has been replaced by long_to_bytes()") - return long_to_bytes(n, blocksize) -def str2long(s): - warnings.warn("str2long() has been replaced by bytes_to_long()") - return bytes_to_long(s) - - -# The first 10000 primes used for checking primality. -# This should be enough to eliminate most of the odd -# numbers before needing to do a Rabin-Miller test at all. -sieve_base = ( - 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, - 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, - 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, - 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, - 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, - 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, - 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, - 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, - 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, - 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, - 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, - 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, - 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, - 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, - 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, - 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, - 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, - 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, - 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, - 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, - 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, - 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, - 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, - 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, - 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, - 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, - 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, - 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, - 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, - 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, - 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, - 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, - 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, - 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, - 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, - 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, - 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, - 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, - 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, - 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, - 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, - 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, - 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, - 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, - 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, - 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, - 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, - 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, - 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, - 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, - 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, - 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, - 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, - 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, - 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989, - 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, - 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, - 4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231, - 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, - 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, - 4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, - 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583, - 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657, - 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, - 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831, - 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937, - 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, - 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, - 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, - 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, - 5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, - 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443, - 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521, - 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, - 5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693, - 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791, - 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, - 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, - 5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053, - 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133, - 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, - 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, - 6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367, - 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473, - 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, - 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673, - 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761, - 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, - 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, - 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997, - 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, - 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, - 7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297, - 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411, - 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, - 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, - 7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643, - 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723, - 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, - 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919, - 7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, 8011, 8017, - 8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111, - 8117, 8123, 8147, 8161, 8167, 8171, 8179, 8191, 8209, 8219, - 8221, 8231, 8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291, - 8293, 8297, 8311, 8317, 8329, 8353, 8363, 8369, 8377, 8387, - 8389, 8419, 8423, 8429, 8431, 8443, 8447, 8461, 8467, 8501, - 8513, 8521, 8527, 8537, 8539, 8543, 8563, 8573, 8581, 8597, - 8599, 8609, 8623, 8627, 8629, 8641, 8647, 8663, 8669, 8677, - 8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737, 8741, - 8747, 8753, 8761, 8779, 8783, 8803, 8807, 8819, 8821, 8831, - 8837, 8839, 8849, 8861, 8863, 8867, 8887, 8893, 8923, 8929, - 8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011, - 9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109, - 9127, 9133, 9137, 9151, 9157, 9161, 9173, 9181, 9187, 9199, - 9203, 9209, 9221, 9227, 9239, 9241, 9257, 9277, 9281, 9283, - 9293, 9311, 9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377, - 9391, 9397, 9403, 9413, 9419, 9421, 9431, 9433, 9437, 9439, - 9461, 9463, 9467, 9473, 9479, 9491, 9497, 9511, 9521, 9533, - 9539, 9547, 9551, 9587, 9601, 9613, 9619, 9623, 9629, 9631, - 9643, 9649, 9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733, - 9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, 9803, 9811, - 9817, 9829, 9833, 9839, 9851, 9857, 9859, 9871, 9883, 9887, - 9901, 9907, 9923, 9929, 9931, 9941, 9949, 9967, 9973, 10007, - 10009, 10037, 10039, 10061, 10067, 10069, 10079, 10091, 10093, 10099, - 10103, 10111, 10133, 10139, 10141, 10151, 10159, 10163, 10169, 10177, - 10181, 10193, 10211, 10223, 10243, 10247, 10253, 10259, 10267, 10271, - 10273, 10289, 10301, 10303, 10313, 10321, 10331, 10333, 10337, 10343, - 10357, 10369, 10391, 10399, 10427, 10429, 10433, 10453, 10457, 10459, - 10463, 10477, 10487, 10499, 10501, 10513, 10529, 10531, 10559, 10567, - 10589, 10597, 10601, 10607, 10613, 10627, 10631, 10639, 10651, 10657, - 10663, 10667, 10687, 10691, 10709, 10711, 10723, 10729, 10733, 10739, - 10753, 10771, 10781, 10789, 10799, 10831, 10837, 10847, 10853, 10859, - 10861, 10867, 10883, 10889, 10891, 10903, 10909, 10937, 10939, 10949, - 10957, 10973, 10979, 10987, 10993, 11003, 11027, 11047, 11057, 11059, - 11069, 11071, 11083, 11087, 11093, 11113, 11117, 11119, 11131, 11149, - 11159, 11161, 11171, 11173, 11177, 11197, 11213, 11239, 11243, 11251, - 11257, 11261, 11273, 11279, 11287, 11299, 11311, 11317, 11321, 11329, - 11351, 11353, 11369, 11383, 11393, 11399, 11411, 11423, 11437, 11443, - 11447, 11467, 11471, 11483, 11489, 11491, 11497, 11503, 11519, 11527, - 11549, 11551, 11579, 11587, 11593, 11597, 11617, 11621, 11633, 11657, - 11677, 11681, 11689, 11699, 11701, 11717, 11719, 11731, 11743, 11777, - 11779, 11783, 11789, 11801, 11807, 11813, 11821, 11827, 11831, 11833, - 11839, 11863, 11867, 11887, 11897, 11903, 11909, 11923, 11927, 11933, - 11939, 11941, 11953, 11959, 11969, 11971, 11981, 11987, 12007, 12011, - 12037, 12041, 12043, 12049, 12071, 12073, 12097, 12101, 12107, 12109, - 12113, 12119, 12143, 12149, 12157, 12161, 12163, 12197, 12203, 12211, - 12227, 12239, 12241, 12251, 12253, 12263, 12269, 12277, 12281, 12289, - 12301, 12323, 12329, 12343, 12347, 12373, 12377, 12379, 12391, 12401, - 12409, 12413, 12421, 12433, 12437, 12451, 12457, 12473, 12479, 12487, - 12491, 12497, 12503, 12511, 12517, 12527, 12539, 12541, 12547, 12553, - 12569, 12577, 12583, 12589, 12601, 12611, 12613, 12619, 12637, 12641, - 12647, 12653, 12659, 12671, 12689, 12697, 12703, 12713, 12721, 12739, - 12743, 12757, 12763, 12781, 12791, 12799, 12809, 12821, 12823, 12829, - 12841, 12853, 12889, 12893, 12899, 12907, 12911, 12917, 12919, 12923, - 12941, 12953, 12959, 12967, 12973, 12979, 12983, 13001, 13003, 13007, - 13009, 13033, 13037, 13043, 13049, 13063, 13093, 13099, 13103, 13109, - 13121, 13127, 13147, 13151, 13159, 13163, 13171, 13177, 13183, 13187, - 13217, 13219, 13229, 13241, 13249, 13259, 13267, 13291, 13297, 13309, - 13313, 13327, 13331, 13337, 13339, 13367, 13381, 13397, 13399, 13411, - 13417, 13421, 13441, 13451, 13457, 13463, 13469, 13477, 13487, 13499, - 13513, 13523, 13537, 13553, 13567, 13577, 13591, 13597, 13613, 13619, - 13627, 13633, 13649, 13669, 13679, 13681, 13687, 13691, 13693, 13697, - 13709, 13711, 13721, 13723, 13729, 13751, 13757, 13759, 13763, 13781, - 13789, 13799, 13807, 13829, 13831, 13841, 13859, 13873, 13877, 13879, - 13883, 13901, 13903, 13907, 13913, 13921, 13931, 13933, 13963, 13967, - 13997, 13999, 14009, 14011, 14029, 14033, 14051, 14057, 14071, 14081, - 14083, 14087, 14107, 14143, 14149, 14153, 14159, 14173, 14177, 14197, - 14207, 14221, 14243, 14249, 14251, 14281, 14293, 14303, 14321, 14323, - 14327, 14341, 14347, 14369, 14387, 14389, 14401, 14407, 14411, 14419, - 14423, 14431, 14437, 14447, 14449, 14461, 14479, 14489, 14503, 14519, - 14533, 14537, 14543, 14549, 14551, 14557, 14561, 14563, 14591, 14593, - 14621, 14627, 14629, 14633, 14639, 14653, 14657, 14669, 14683, 14699, - 14713, 14717, 14723, 14731, 14737, 14741, 14747, 14753, 14759, 14767, - 14771, 14779, 14783, 14797, 14813, 14821, 14827, 14831, 14843, 14851, - 14867, 14869, 14879, 14887, 14891, 14897, 14923, 14929, 14939, 14947, - 14951, 14957, 14969, 14983, 15013, 15017, 15031, 15053, 15061, 15073, - 15077, 15083, 15091, 15101, 15107, 15121, 15131, 15137, 15139, 15149, - 15161, 15173, 15187, 15193, 15199, 15217, 15227, 15233, 15241, 15259, - 15263, 15269, 15271, 15277, 15287, 15289, 15299, 15307, 15313, 15319, - 15329, 15331, 15349, 15359, 15361, 15373, 15377, 15383, 15391, 15401, - 15413, 15427, 15439, 15443, 15451, 15461, 15467, 15473, 15493, 15497, - 15511, 15527, 15541, 15551, 15559, 15569, 15581, 15583, 15601, 15607, - 15619, 15629, 15641, 15643, 15647, 15649, 15661, 15667, 15671, 15679, - 15683, 15727, 15731, 15733, 15737, 15739, 15749, 15761, 15767, 15773, - 15787, 15791, 15797, 15803, 15809, 15817, 15823, 15859, 15877, 15881, - 15887, 15889, 15901, 15907, 15913, 15919, 15923, 15937, 15959, 15971, - 15973, 15991, 16001, 16007, 16033, 16057, 16061, 16063, 16067, 16069, - 16073, 16087, 16091, 16097, 16103, 16111, 16127, 16139, 16141, 16183, - 16187, 16189, 16193, 16217, 16223, 16229, 16231, 16249, 16253, 16267, - 16273, 16301, 16319, 16333, 16339, 16349, 16361, 16363, 16369, 16381, - 16411, 16417, 16421, 16427, 16433, 16447, 16451, 16453, 16477, 16481, - 16487, 16493, 16519, 16529, 16547, 16553, 16561, 16567, 16573, 16603, - 16607, 16619, 16631, 16633, 16649, 16651, 16657, 16661, 16673, 16691, - 16693, 16699, 16703, 16729, 16741, 16747, 16759, 16763, 16787, 16811, - 16823, 16829, 16831, 16843, 16871, 16879, 16883, 16889, 16901, 16903, - 16921, 16927, 16931, 16937, 16943, 16963, 16979, 16981, 16987, 16993, - 17011, 17021, 17027, 17029, 17033, 17041, 17047, 17053, 17077, 17093, - 17099, 17107, 17117, 17123, 17137, 17159, 17167, 17183, 17189, 17191, - 17203, 17207, 17209, 17231, 17239, 17257, 17291, 17293, 17299, 17317, - 17321, 17327, 17333, 17341, 17351, 17359, 17377, 17383, 17387, 17389, - 17393, 17401, 17417, 17419, 17431, 17443, 17449, 17467, 17471, 17477, - 17483, 17489, 17491, 17497, 17509, 17519, 17539, 17551, 17569, 17573, - 17579, 17581, 17597, 17599, 17609, 17623, 17627, 17657, 17659, 17669, - 17681, 17683, 17707, 17713, 17729, 17737, 17747, 17749, 17761, 17783, - 17789, 17791, 17807, 17827, 17837, 17839, 17851, 17863, 17881, 17891, - 17903, 17909, 17911, 17921, 17923, 17929, 17939, 17957, 17959, 17971, - 17977, 17981, 17987, 17989, 18013, 18041, 18043, 18047, 18049, 18059, - 18061, 18077, 18089, 18097, 18119, 18121, 18127, 18131, 18133, 18143, - 18149, 18169, 18181, 18191, 18199, 18211, 18217, 18223, 18229, 18233, - 18251, 18253, 18257, 18269, 18287, 18289, 18301, 18307, 18311, 18313, - 18329, 18341, 18353, 18367, 18371, 18379, 18397, 18401, 18413, 18427, - 18433, 18439, 18443, 18451, 18457, 18461, 18481, 18493, 18503, 18517, - 18521, 18523, 18539, 18541, 18553, 18583, 18587, 18593, 18617, 18637, - 18661, 18671, 18679, 18691, 18701, 18713, 18719, 18731, 18743, 18749, - 18757, 18773, 18787, 18793, 18797, 18803, 18839, 18859, 18869, 18899, - 18911, 18913, 18917, 18919, 18947, 18959, 18973, 18979, 19001, 19009, - 19013, 19031, 19037, 19051, 19069, 19073, 19079, 19081, 19087, 19121, - 19139, 19141, 19157, 19163, 19181, 19183, 19207, 19211, 19213, 19219, - 19231, 19237, 19249, 19259, 19267, 19273, 19289, 19301, 19309, 19319, - 19333, 19373, 19379, 19381, 19387, 19391, 19403, 19417, 19421, 19423, - 19427, 19429, 19433, 19441, 19447, 19457, 19463, 19469, 19471, 19477, - 19483, 19489, 19501, 19507, 19531, 19541, 19543, 19553, 19559, 19571, - 19577, 19583, 19597, 19603, 19609, 19661, 19681, 19687, 19697, 19699, - 19709, 19717, 19727, 19739, 19751, 19753, 19759, 19763, 19777, 19793, - 19801, 19813, 19819, 19841, 19843, 19853, 19861, 19867, 19889, 19891, - 19913, 19919, 19927, 19937, 19949, 19961, 19963, 19973, 19979, 19991, - 19993, 19997, 20011, 20021, 20023, 20029, 20047, 20051, 20063, 20071, - 20089, 20101, 20107, 20113, 20117, 20123, 20129, 20143, 20147, 20149, - 20161, 20173, 20177, 20183, 20201, 20219, 20231, 20233, 20249, 20261, - 20269, 20287, 20297, 20323, 20327, 20333, 20341, 20347, 20353, 20357, - 20359, 20369, 20389, 20393, 20399, 20407, 20411, 20431, 20441, 20443, - 20477, 20479, 20483, 20507, 20509, 20521, 20533, 20543, 20549, 20551, - 20563, 20593, 20599, 20611, 20627, 20639, 20641, 20663, 20681, 20693, - 20707, 20717, 20719, 20731, 20743, 20747, 20749, 20753, 20759, 20771, - 20773, 20789, 20807, 20809, 20849, 20857, 20873, 20879, 20887, 20897, - 20899, 20903, 20921, 20929, 20939, 20947, 20959, 20963, 20981, 20983, - 21001, 21011, 21013, 21017, 21019, 21023, 21031, 21059, 21061, 21067, - 21089, 21101, 21107, 21121, 21139, 21143, 21149, 21157, 21163, 21169, - 21179, 21187, 21191, 21193, 21211, 21221, 21227, 21247, 21269, 21277, - 21283, 21313, 21317, 21319, 21323, 21341, 21347, 21377, 21379, 21383, - 21391, 21397, 21401, 21407, 21419, 21433, 21467, 21481, 21487, 21491, - 21493, 21499, 21503, 21517, 21521, 21523, 21529, 21557, 21559, 21563, - 21569, 21577, 21587, 21589, 21599, 21601, 21611, 21613, 21617, 21647, - 21649, 21661, 21673, 21683, 21701, 21713, 21727, 21737, 21739, 21751, - 21757, 21767, 21773, 21787, 21799, 21803, 21817, 21821, 21839, 21841, - 21851, 21859, 21863, 21871, 21881, 21893, 21911, 21929, 21937, 21943, - 21961, 21977, 21991, 21997, 22003, 22013, 22027, 22031, 22037, 22039, - 22051, 22063, 22067, 22073, 22079, 22091, 22093, 22109, 22111, 22123, - 22129, 22133, 22147, 22153, 22157, 22159, 22171, 22189, 22193, 22229, - 22247, 22259, 22271, 22273, 22277, 22279, 22283, 22291, 22303, 22307, - 22343, 22349, 22367, 22369, 22381, 22391, 22397, 22409, 22433, 22441, - 22447, 22453, 22469, 22481, 22483, 22501, 22511, 22531, 22541, 22543, - 22549, 22567, 22571, 22573, 22613, 22619, 22621, 22637, 22639, 22643, - 22651, 22669, 22679, 22691, 22697, 22699, 22709, 22717, 22721, 22727, - 22739, 22741, 22751, 22769, 22777, 22783, 22787, 22807, 22811, 22817, - 22853, 22859, 22861, 22871, 22877, 22901, 22907, 22921, 22937, 22943, - 22961, 22963, 22973, 22993, 23003, 23011, 23017, 23021, 23027, 23029, - 23039, 23041, 23053, 23057, 23059, 23063, 23071, 23081, 23087, 23099, - 23117, 23131, 23143, 23159, 23167, 23173, 23189, 23197, 23201, 23203, - 23209, 23227, 23251, 23269, 23279, 23291, 23293, 23297, 23311, 23321, - 23327, 23333, 23339, 23357, 23369, 23371, 23399, 23417, 23431, 23447, - 23459, 23473, 23497, 23509, 23531, 23537, 23539, 23549, 23557, 23561, - 23563, 23567, 23581, 23593, 23599, 23603, 23609, 23623, 23627, 23629, - 23633, 23663, 23669, 23671, 23677, 23687, 23689, 23719, 23741, 23743, - 23747, 23753, 23761, 23767, 23773, 23789, 23801, 23813, 23819, 23827, - 23831, 23833, 23857, 23869, 23873, 23879, 23887, 23893, 23899, 23909, - 23911, 23917, 23929, 23957, 23971, 23977, 23981, 23993, 24001, 24007, - 24019, 24023, 24029, 24043, 24049, 24061, 24071, 24077, 24083, 24091, - 24097, 24103, 24107, 24109, 24113, 24121, 24133, 24137, 24151, 24169, - 24179, 24181, 24197, 24203, 24223, 24229, 24239, 24247, 24251, 24281, - 24317, 24329, 24337, 24359, 24371, 24373, 24379, 24391, 24407, 24413, - 24419, 24421, 24439, 24443, 24469, 24473, 24481, 24499, 24509, 24517, - 24527, 24533, 24547, 24551, 24571, 24593, 24611, 24623, 24631, 24659, - 24671, 24677, 24683, 24691, 24697, 24709, 24733, 24749, 24763, 24767, - 24781, 24793, 24799, 24809, 24821, 24841, 24847, 24851, 24859, 24877, - 24889, 24907, 24917, 24919, 24923, 24943, 24953, 24967, 24971, 24977, - 24979, 24989, 25013, 25031, 25033, 25037, 25057, 25073, 25087, 25097, - 25111, 25117, 25121, 25127, 25147, 25153, 25163, 25169, 25171, 25183, - 25189, 25219, 25229, 25237, 25243, 25247, 25253, 25261, 25301, 25303, - 25307, 25309, 25321, 25339, 25343, 25349, 25357, 25367, 25373, 25391, - 25409, 25411, 25423, 25439, 25447, 25453, 25457, 25463, 25469, 25471, - 25523, 25537, 25541, 25561, 25577, 25579, 25583, 25589, 25601, 25603, - 25609, 25621, 25633, 25639, 25643, 25657, 25667, 25673, 25679, 25693, - 25703, 25717, 25733, 25741, 25747, 25759, 25763, 25771, 25793, 25799, - 25801, 25819, 25841, 25847, 25849, 25867, 25873, 25889, 25903, 25913, - 25919, 25931, 25933, 25939, 25943, 25951, 25969, 25981, 25997, 25999, - 26003, 26017, 26021, 26029, 26041, 26053, 26083, 26099, 26107, 26111, - 26113, 26119, 26141, 26153, 26161, 26171, 26177, 26183, 26189, 26203, - 26209, 26227, 26237, 26249, 26251, 26261, 26263, 26267, 26293, 26297, - 26309, 26317, 26321, 26339, 26347, 26357, 26371, 26387, 26393, 26399, - 26407, 26417, 26423, 26431, 26437, 26449, 26459, 26479, 26489, 26497, - 26501, 26513, 26539, 26557, 26561, 26573, 26591, 26597, 26627, 26633, - 26641, 26647, 26669, 26681, 26683, 26687, 26693, 26699, 26701, 26711, - 26713, 26717, 26723, 26729, 26731, 26737, 26759, 26777, 26783, 26801, - 26813, 26821, 26833, 26839, 26849, 26861, 26863, 26879, 26881, 26891, - 26893, 26903, 26921, 26927, 26947, 26951, 26953, 26959, 26981, 26987, - 26993, 27011, 27017, 27031, 27043, 27059, 27061, 27067, 27073, 27077, - 27091, 27103, 27107, 27109, 27127, 27143, 27179, 27191, 27197, 27211, - 27239, 27241, 27253, 27259, 27271, 27277, 27281, 27283, 27299, 27329, - 27337, 27361, 27367, 27397, 27407, 27409, 27427, 27431, 27437, 27449, - 27457, 27479, 27481, 27487, 27509, 27527, 27529, 27539, 27541, 27551, - 27581, 27583, 27611, 27617, 27631, 27647, 27653, 27673, 27689, 27691, - 27697, 27701, 27733, 27737, 27739, 27743, 27749, 27751, 27763, 27767, - 27773, 27779, 27791, 27793, 27799, 27803, 27809, 27817, 27823, 27827, - 27847, 27851, 27883, 27893, 27901, 27917, 27919, 27941, 27943, 27947, - 27953, 27961, 27967, 27983, 27997, 28001, 28019, 28027, 28031, 28051, - 28057, 28069, 28081, 28087, 28097, 28099, 28109, 28111, 28123, 28151, - 28163, 28181, 28183, 28201, 28211, 28219, 28229, 28277, 28279, 28283, - 28289, 28297, 28307, 28309, 28319, 28349, 28351, 28387, 28393, 28403, - 28409, 28411, 28429, 28433, 28439, 28447, 28463, 28477, 28493, 28499, - 28513, 28517, 28537, 28541, 28547, 28549, 28559, 28571, 28573, 28579, - 28591, 28597, 28603, 28607, 28619, 28621, 28627, 28631, 28643, 28649, - 28657, 28661, 28663, 28669, 28687, 28697, 28703, 28711, 28723, 28729, - 28751, 28753, 28759, 28771, 28789, 28793, 28807, 28813, 28817, 28837, - 28843, 28859, 28867, 28871, 28879, 28901, 28909, 28921, 28927, 28933, - 28949, 28961, 28979, 29009, 29017, 29021, 29023, 29027, 29033, 29059, - 29063, 29077, 29101, 29123, 29129, 29131, 29137, 29147, 29153, 29167, - 29173, 29179, 29191, 29201, 29207, 29209, 29221, 29231, 29243, 29251, - 29269, 29287, 29297, 29303, 29311, 29327, 29333, 29339, 29347, 29363, - 29383, 29387, 29389, 29399, 29401, 29411, 29423, 29429, 29437, 29443, - 29453, 29473, 29483, 29501, 29527, 29531, 29537, 29567, 29569, 29573, - 29581, 29587, 29599, 29611, 29629, 29633, 29641, 29663, 29669, 29671, - 29683, 29717, 29723, 29741, 29753, 29759, 29761, 29789, 29803, 29819, - 29833, 29837, 29851, 29863, 29867, 29873, 29879, 29881, 29917, 29921, - 29927, 29947, 29959, 29983, 29989, 30011, 30013, 30029, 30047, 30059, - 30071, 30089, 30091, 30097, 30103, 30109, 30113, 30119, 30133, 30137, - 30139, 30161, 30169, 30181, 30187, 30197, 30203, 30211, 30223, 30241, - 30253, 30259, 30269, 30271, 30293, 30307, 30313, 30319, 30323, 30341, - 30347, 30367, 30389, 30391, 30403, 30427, 30431, 30449, 30467, 30469, - 30491, 30493, 30497, 30509, 30517, 30529, 30539, 30553, 30557, 30559, - 30577, 30593, 30631, 30637, 30643, 30649, 30661, 30671, 30677, 30689, - 30697, 30703, 30707, 30713, 30727, 30757, 30763, 30773, 30781, 30803, - 30809, 30817, 30829, 30839, 30841, 30851, 30853, 30859, 30869, 30871, - 30881, 30893, 30911, 30931, 30937, 30941, 30949, 30971, 30977, 30983, - 31013, 31019, 31033, 31039, 31051, 31063, 31069, 31079, 31081, 31091, - 31121, 31123, 31139, 31147, 31151, 31153, 31159, 31177, 31181, 31183, - 31189, 31193, 31219, 31223, 31231, 31237, 31247, 31249, 31253, 31259, - 31267, 31271, 31277, 31307, 31319, 31321, 31327, 31333, 31337, 31357, - 31379, 31387, 31391, 31393, 31397, 31469, 31477, 31481, 31489, 31511, - 31513, 31517, 31531, 31541, 31543, 31547, 31567, 31573, 31583, 31601, - 31607, 31627, 31643, 31649, 31657, 31663, 31667, 31687, 31699, 31721, - 31723, 31727, 31729, 31741, 31751, 31769, 31771, 31793, 31799, 31817, - 31847, 31849, 31859, 31873, 31883, 31891, 31907, 31957, 31963, 31973, - 31981, 31991, 32003, 32009, 32027, 32029, 32051, 32057, 32059, 32063, - 32069, 32077, 32083, 32089, 32099, 32117, 32119, 32141, 32143, 32159, - 32173, 32183, 32189, 32191, 32203, 32213, 32233, 32237, 32251, 32257, - 32261, 32297, 32299, 32303, 32309, 32321, 32323, 32327, 32341, 32353, - 32359, 32363, 32369, 32371, 32377, 32381, 32401, 32411, 32413, 32423, - 32429, 32441, 32443, 32467, 32479, 32491, 32497, 32503, 32507, 32531, - 32533, 32537, 32561, 32563, 32569, 32573, 32579, 32587, 32603, 32609, - 32611, 32621, 32633, 32647, 32653, 32687, 32693, 32707, 32713, 32717, - 32719, 32749, 32771, 32779, 32783, 32789, 32797, 32801, 32803, 32831, - 32833, 32839, 32843, 32869, 32887, 32909, 32911, 32917, 32933, 32939, - 32941, 32957, 32969, 32971, 32983, 32987, 32993, 32999, 33013, 33023, - 33029, 33037, 33049, 33053, 33071, 33073, 33083, 33091, 33107, 33113, - 33119, 33149, 33151, 33161, 33179, 33181, 33191, 33199, 33203, 33211, - 33223, 33247, 33287, 33289, 33301, 33311, 33317, 33329, 33331, 33343, - 33347, 33349, 33353, 33359, 33377, 33391, 33403, 33409, 33413, 33427, - 33457, 33461, 33469, 33479, 33487, 33493, 33503, 33521, 33529, 33533, - 33547, 33563, 33569, 33577, 33581, 33587, 33589, 33599, 33601, 33613, - 33617, 33619, 33623, 33629, 33637, 33641, 33647, 33679, 33703, 33713, - 33721, 33739, 33749, 33751, 33757, 33767, 33769, 33773, 33791, 33797, - 33809, 33811, 33827, 33829, 33851, 33857, 33863, 33871, 33889, 33893, - 33911, 33923, 33931, 33937, 33941, 33961, 33967, 33997, 34019, 34031, - 34033, 34039, 34057, 34061, 34123, 34127, 34129, 34141, 34147, 34157, - 34159, 34171, 34183, 34211, 34213, 34217, 34231, 34253, 34259, 34261, - 34267, 34273, 34283, 34297, 34301, 34303, 34313, 34319, 34327, 34337, - 34351, 34361, 34367, 34369, 34381, 34403, 34421, 34429, 34439, 34457, - 34469, 34471, 34483, 34487, 34499, 34501, 34511, 34513, 34519, 34537, - 34543, 34549, 34583, 34589, 34591, 34603, 34607, 34613, 34631, 34649, - 34651, 34667, 34673, 34679, 34687, 34693, 34703, 34721, 34729, 34739, - 34747, 34757, 34759, 34763, 34781, 34807, 34819, 34841, 34843, 34847, - 34849, 34871, 34877, 34883, 34897, 34913, 34919, 34939, 34949, 34961, - 34963, 34981, 35023, 35027, 35051, 35053, 35059, 35069, 35081, 35083, - 35089, 35099, 35107, 35111, 35117, 35129, 35141, 35149, 35153, 35159, - 35171, 35201, 35221, 35227, 35251, 35257, 35267, 35279, 35281, 35291, - 35311, 35317, 35323, 35327, 35339, 35353, 35363, 35381, 35393, 35401, - 35407, 35419, 35423, 35437, 35447, 35449, 35461, 35491, 35507, 35509, - 35521, 35527, 35531, 35533, 35537, 35543, 35569, 35573, 35591, 35593, - 35597, 35603, 35617, 35671, 35677, 35729, 35731, 35747, 35753, 35759, - 35771, 35797, 35801, 35803, 35809, 35831, 35837, 35839, 35851, 35863, - 35869, 35879, 35897, 35899, 35911, 35923, 35933, 35951, 35963, 35969, - 35977, 35983, 35993, 35999, 36007, 36011, 36013, 36017, 36037, 36061, - 36067, 36073, 36083, 36097, 36107, 36109, 36131, 36137, 36151, 36161, - 36187, 36191, 36209, 36217, 36229, 36241, 36251, 36263, 36269, 36277, - 36293, 36299, 36307, 36313, 36319, 36341, 36343, 36353, 36373, 36383, - 36389, 36433, 36451, 36457, 36467, 36469, 36473, 36479, 36493, 36497, - 36523, 36527, 36529, 36541, 36551, 36559, 36563, 36571, 36583, 36587, - 36599, 36607, 36629, 36637, 36643, 36653, 36671, 36677, 36683, 36691, - 36697, 36709, 36713, 36721, 36739, 36749, 36761, 36767, 36779, 36781, - 36787, 36791, 36793, 36809, 36821, 36833, 36847, 36857, 36871, 36877, - 36887, 36899, 36901, 36913, 36919, 36923, 36929, 36931, 36943, 36947, - 36973, 36979, 36997, 37003, 37013, 37019, 37021, 37039, 37049, 37057, - 37061, 37087, 37097, 37117, 37123, 37139, 37159, 37171, 37181, 37189, - 37199, 37201, 37217, 37223, 37243, 37253, 37273, 37277, 37307, 37309, - 37313, 37321, 37337, 37339, 37357, 37361, 37363, 37369, 37379, 37397, - 37409, 37423, 37441, 37447, 37463, 37483, 37489, 37493, 37501, 37507, - 37511, 37517, 37529, 37537, 37547, 37549, 37561, 37567, 37571, 37573, - 37579, 37589, 37591, 37607, 37619, 37633, 37643, 37649, 37657, 37663, - 37691, 37693, 37699, 37717, 37747, 37781, 37783, 37799, 37811, 37813, - 37831, 37847, 37853, 37861, 37871, 37879, 37889, 37897, 37907, 37951, - 37957, 37963, 37967, 37987, 37991, 37993, 37997, 38011, 38039, 38047, - 38053, 38069, 38083, 38113, 38119, 38149, 38153, 38167, 38177, 38183, - 38189, 38197, 38201, 38219, 38231, 38237, 38239, 38261, 38273, 38281, - 38287, 38299, 38303, 38317, 38321, 38327, 38329, 38333, 38351, 38371, - 38377, 38393, 38431, 38447, 38449, 38453, 38459, 38461, 38501, 38543, - 38557, 38561, 38567, 38569, 38593, 38603, 38609, 38611, 38629, 38639, - 38651, 38653, 38669, 38671, 38677, 38693, 38699, 38707, 38711, 38713, - 38723, 38729, 38737, 38747, 38749, 38767, 38783, 38791, 38803, 38821, - 38833, 38839, 38851, 38861, 38867, 38873, 38891, 38903, 38917, 38921, - 38923, 38933, 38953, 38959, 38971, 38977, 38993, 39019, 39023, 39041, - 39043, 39047, 39079, 39089, 39097, 39103, 39107, 39113, 39119, 39133, - 39139, 39157, 39161, 39163, 39181, 39191, 39199, 39209, 39217, 39227, - 39229, 39233, 39239, 39241, 39251, 39293, 39301, 39313, 39317, 39323, - 39341, 39343, 39359, 39367, 39371, 39373, 39383, 39397, 39409, 39419, - 39439, 39443, 39451, 39461, 39499, 39503, 39509, 39511, 39521, 39541, - 39551, 39563, 39569, 39581, 39607, 39619, 39623, 39631, 39659, 39667, - 39671, 39679, 39703, 39709, 39719, 39727, 39733, 39749, 39761, 39769, - 39779, 39791, 39799, 39821, 39827, 39829, 39839, 39841, 39847, 39857, - 39863, 39869, 39877, 39883, 39887, 39901, 39929, 39937, 39953, 39971, - 39979, 39983, 39989, 40009, 40013, 40031, 40037, 40039, 40063, 40087, - 40093, 40099, 40111, 40123, 40127, 40129, 40151, 40153, 40163, 40169, - 40177, 40189, 40193, 40213, 40231, 40237, 40241, 40253, 40277, 40283, - 40289, 40343, 40351, 40357, 40361, 40387, 40423, 40427, 40429, 40433, - 40459, 40471, 40483, 40487, 40493, 40499, 40507, 40519, 40529, 40531, - 40543, 40559, 40577, 40583, 40591, 40597, 40609, 40627, 40637, 40639, - 40693, 40697, 40699, 40709, 40739, 40751, 40759, 40763, 40771, 40787, - 40801, 40813, 40819, 40823, 40829, 40841, 40847, 40849, 40853, 40867, - 40879, 40883, 40897, 40903, 40927, 40933, 40939, 40949, 40961, 40973, - 40993, 41011, 41017, 41023, 41039, 41047, 41051, 41057, 41077, 41081, - 41113, 41117, 41131, 41141, 41143, 41149, 41161, 41177, 41179, 41183, - 41189, 41201, 41203, 41213, 41221, 41227, 41231, 41233, 41243, 41257, - 41263, 41269, 41281, 41299, 41333, 41341, 41351, 41357, 41381, 41387, - 41389, 41399, 41411, 41413, 41443, 41453, 41467, 41479, 41491, 41507, - 41513, 41519, 41521, 41539, 41543, 41549, 41579, 41593, 41597, 41603, - 41609, 41611, 41617, 41621, 41627, 41641, 41647, 41651, 41659, 41669, - 41681, 41687, 41719, 41729, 41737, 41759, 41761, 41771, 41777, 41801, - 41809, 41813, 41843, 41849, 41851, 41863, 41879, 41887, 41893, 41897, - 41903, 41911, 41927, 41941, 41947, 41953, 41957, 41959, 41969, 41981, - 41983, 41999, 42013, 42017, 42019, 42023, 42043, 42061, 42071, 42073, - 42083, 42089, 42101, 42131, 42139, 42157, 42169, 42179, 42181, 42187, - 42193, 42197, 42209, 42221, 42223, 42227, 42239, 42257, 42281, 42283, - 42293, 42299, 42307, 42323, 42331, 42337, 42349, 42359, 42373, 42379, - 42391, 42397, 42403, 42407, 42409, 42433, 42437, 42443, 42451, 42457, - 42461, 42463, 42467, 42473, 42487, 42491, 42499, 42509, 42533, 42557, - 42569, 42571, 42577, 42589, 42611, 42641, 42643, 42649, 42667, 42677, - 42683, 42689, 42697, 42701, 42703, 42709, 42719, 42727, 42737, 42743, - 42751, 42767, 42773, 42787, 42793, 42797, 42821, 42829, 42839, 42841, - 42853, 42859, 42863, 42899, 42901, 42923, 42929, 42937, 42943, 42953, - 42961, 42967, 42979, 42989, 43003, 43013, 43019, 43037, 43049, 43051, - 43063, 43067, 43093, 43103, 43117, 43133, 43151, 43159, 43177, 43189, - 43201, 43207, 43223, 43237, 43261, 43271, 43283, 43291, 43313, 43319, - 43321, 43331, 43391, 43397, 43399, 43403, 43411, 43427, 43441, 43451, - 43457, 43481, 43487, 43499, 43517, 43541, 43543, 43573, 43577, 43579, - 43591, 43597, 43607, 43609, 43613, 43627, 43633, 43649, 43651, 43661, - 43669, 43691, 43711, 43717, 43721, 43753, 43759, 43777, 43781, 43783, - 43787, 43789, 43793, 43801, 43853, 43867, 43889, 43891, 43913, 43933, - 43943, 43951, 43961, 43963, 43969, 43973, 43987, 43991, 43997, 44017, - 44021, 44027, 44029, 44041, 44053, 44059, 44071, 44087, 44089, 44101, - 44111, 44119, 44123, 44129, 44131, 44159, 44171, 44179, 44189, 44201, - 44203, 44207, 44221, 44249, 44257, 44263, 44267, 44269, 44273, 44279, - 44281, 44293, 44351, 44357, 44371, 44381, 44383, 44389, 44417, 44449, - 44453, 44483, 44491, 44497, 44501, 44507, 44519, 44531, 44533, 44537, - 44543, 44549, 44563, 44579, 44587, 44617, 44621, 44623, 44633, 44641, - 44647, 44651, 44657, 44683, 44687, 44699, 44701, 44711, 44729, 44741, - 44753, 44771, 44773, 44777, 44789, 44797, 44809, 44819, 44839, 44843, - 44851, 44867, 44879, 44887, 44893, 44909, 44917, 44927, 44939, 44953, - 44959, 44963, 44971, 44983, 44987, 45007, 45013, 45053, 45061, 45077, - 45083, 45119, 45121, 45127, 45131, 45137, 45139, 45161, 45179, 45181, - 45191, 45197, 45233, 45247, 45259, 45263, 45281, 45289, 45293, 45307, - 45317, 45319, 45329, 45337, 45341, 45343, 45361, 45377, 45389, 45403, - 45413, 45427, 45433, 45439, 45481, 45491, 45497, 45503, 45523, 45533, - 45541, 45553, 45557, 45569, 45587, 45589, 45599, 45613, 45631, 45641, - 45659, 45667, 45673, 45677, 45691, 45697, 45707, 45737, 45751, 45757, - 45763, 45767, 45779, 45817, 45821, 45823, 45827, 45833, 45841, 45853, - 45863, 45869, 45887, 45893, 45943, 45949, 45953, 45959, 45971, 45979, - 45989, 46021, 46027, 46049, 46051, 46061, 46073, 46091, 46093, 46099, - 46103, 46133, 46141, 46147, 46153, 46171, 46181, 46183, 46187, 46199, - 46219, 46229, 46237, 46261, 46271, 46273, 46279, 46301, 46307, 46309, - 46327, 46337, 46349, 46351, 46381, 46399, 46411, 46439, 46441, 46447, - 46451, 46457, 46471, 46477, 46489, 46499, 46507, 46511, 46523, 46549, - 46559, 46567, 46573, 46589, 46591, 46601, 46619, 46633, 46639, 46643, - 46649, 46663, 46679, 46681, 46687, 46691, 46703, 46723, 46727, 46747, - 46751, 46757, 46769, 46771, 46807, 46811, 46817, 46819, 46829, 46831, - 46853, 46861, 46867, 46877, 46889, 46901, 46919, 46933, 46957, 46993, - 46997, 47017, 47041, 47051, 47057, 47059, 47087, 47093, 47111, 47119, - 47123, 47129, 47137, 47143, 47147, 47149, 47161, 47189, 47207, 47221, - 47237, 47251, 47269, 47279, 47287, 47293, 47297, 47303, 47309, 47317, - 47339, 47351, 47353, 47363, 47381, 47387, 47389, 47407, 47417, 47419, - 47431, 47441, 47459, 47491, 47497, 47501, 47507, 47513, 47521, 47527, - 47533, 47543, 47563, 47569, 47581, 47591, 47599, 47609, 47623, 47629, - 47639, 47653, 47657, 47659, 47681, 47699, 47701, 47711, 47713, 47717, - 47737, 47741, 47743, 47777, 47779, 47791, 47797, 47807, 47809, 47819, - 47837, 47843, 47857, 47869, 47881, 47903, 47911, 47917, 47933, 47939, - 47947, 47951, 47963, 47969, 47977, 47981, 48017, 48023, 48029, 48049, - 48073, 48079, 48091, 48109, 48119, 48121, 48131, 48157, 48163, 48179, - 48187, 48193, 48197, 48221, 48239, 48247, 48259, 48271, 48281, 48299, - 48311, 48313, 48337, 48341, 48353, 48371, 48383, 48397, 48407, 48409, - 48413, 48437, 48449, 48463, 48473, 48479, 48481, 48487, 48491, 48497, - 48523, 48527, 48533, 48539, 48541, 48563, 48571, 48589, 48593, 48611, - 48619, 48623, 48647, 48649, 48661, 48673, 48677, 48679, 48731, 48733, - 48751, 48757, 48761, 48767, 48779, 48781, 48787, 48799, 48809, 48817, - 48821, 48823, 48847, 48857, 48859, 48869, 48871, 48883, 48889, 48907, - 48947, 48953, 48973, 48989, 48991, 49003, 49009, 49019, 49031, 49033, - 49037, 49043, 49057, 49069, 49081, 49103, 49109, 49117, 49121, 49123, - 49139, 49157, 49169, 49171, 49177, 49193, 49199, 49201, 49207, 49211, - 49223, 49253, 49261, 49277, 49279, 49297, 49307, 49331, 49333, 49339, - 49363, 49367, 49369, 49391, 49393, 49409, 49411, 49417, 49429, 49433, - 49451, 49459, 49463, 49477, 49481, 49499, 49523, 49529, 49531, 49537, - 49547, 49549, 49559, 49597, 49603, 49613, 49627, 49633, 49639, 49663, - 49667, 49669, 49681, 49697, 49711, 49727, 49739, 49741, 49747, 49757, - 49783, 49787, 49789, 49801, 49807, 49811, 49823, 49831, 49843, 49853, - 49871, 49877, 49891, 49919, 49921, 49927, 49937, 49939, 49943, 49957, - 49991, 49993, 49999, 50021, 50023, 50033, 50047, 50051, 50053, 50069, - 50077, 50087, 50093, 50101, 50111, 50119, 50123, 50129, 50131, 50147, - 50153, 50159, 50177, 50207, 50221, 50227, 50231, 50261, 50263, 50273, - 50287, 50291, 50311, 50321, 50329, 50333, 50341, 50359, 50363, 50377, - 50383, 50387, 50411, 50417, 50423, 50441, 50459, 50461, 50497, 50503, - 50513, 50527, 50539, 50543, 50549, 50551, 50581, 50587, 50591, 50593, - 50599, 50627, 50647, 50651, 50671, 50683, 50707, 50723, 50741, 50753, - 50767, 50773, 50777, 50789, 50821, 50833, 50839, 50849, 50857, 50867, - 50873, 50891, 50893, 50909, 50923, 50929, 50951, 50957, 50969, 50971, - 50989, 50993, 51001, 51031, 51043, 51047, 51059, 51061, 51071, 51109, - 51131, 51133, 51137, 51151, 51157, 51169, 51193, 51197, 51199, 51203, - 51217, 51229, 51239, 51241, 51257, 51263, 51283, 51287, 51307, 51329, - 51341, 51343, 51347, 51349, 51361, 51383, 51407, 51413, 51419, 51421, - 51427, 51431, 51437, 51439, 51449, 51461, 51473, 51479, 51481, 51487, - 51503, 51511, 51517, 51521, 51539, 51551, 51563, 51577, 51581, 51593, - 51599, 51607, 51613, 51631, 51637, 51647, 51659, 51673, 51679, 51683, - 51691, 51713, 51719, 51721, 51749, 51767, 51769, 51787, 51797, 51803, - 51817, 51827, 51829, 51839, 51853, 51859, 51869, 51871, 51893, 51899, - 51907, 51913, 51929, 51941, 51949, 51971, 51973, 51977, 51991, 52009, - 52021, 52027, 52051, 52057, 52067, 52069, 52081, 52103, 52121, 52127, - 52147, 52153, 52163, 52177, 52181, 52183, 52189, 52201, 52223, 52237, - 52249, 52253, 52259, 52267, 52289, 52291, 52301, 52313, 52321, 52361, - 52363, 52369, 52379, 52387, 52391, 52433, 52453, 52457, 52489, 52501, - 52511, 52517, 52529, 52541, 52543, 52553, 52561, 52567, 52571, 52579, - 52583, 52609, 52627, 52631, 52639, 52667, 52673, 52691, 52697, 52709, - 52711, 52721, 52727, 52733, 52747, 52757, 52769, 52783, 52807, 52813, - 52817, 52837, 52859, 52861, 52879, 52883, 52889, 52901, 52903, 52919, - 52937, 52951, 52957, 52963, 52967, 52973, 52981, 52999, 53003, 53017, - 53047, 53051, 53069, 53077, 53087, 53089, 53093, 53101, 53113, 53117, - 53129, 53147, 53149, 53161, 53171, 53173, 53189, 53197, 53201, 53231, - 53233, 53239, 53267, 53269, 53279, 53281, 53299, 53309, 53323, 53327, - 53353, 53359, 53377, 53381, 53401, 53407, 53411, 53419, 53437, 53441, - 53453, 53479, 53503, 53507, 53527, 53549, 53551, 53569, 53591, 53593, - 53597, 53609, 53611, 53617, 53623, 53629, 53633, 53639, 53653, 53657, - 53681, 53693, 53699, 53717, 53719, 53731, 53759, 53773, 53777, 53783, - 53791, 53813, 53819, 53831, 53849, 53857, 53861, 53881, 53887, 53891, - 53897, 53899, 53917, 53923, 53927, 53939, 53951, 53959, 53987, 53993, - 54001, 54011, 54013, 54037, 54049, 54059, 54083, 54091, 54101, 54121, - 54133, 54139, 54151, 54163, 54167, 54181, 54193, 54217, 54251, 54269, - 54277, 54287, 54293, 54311, 54319, 54323, 54331, 54347, 54361, 54367, - 54371, 54377, 54401, 54403, 54409, 54413, 54419, 54421, 54437, 54443, - 54449, 54469, 54493, 54497, 54499, 54503, 54517, 54521, 54539, 54541, - 54547, 54559, 54563, 54577, 54581, 54583, 54601, 54617, 54623, 54629, - 54631, 54647, 54667, 54673, 54679, 54709, 54713, 54721, 54727, 54751, - 54767, 54773, 54779, 54787, 54799, 54829, 54833, 54851, 54869, 54877, - 54881, 54907, 54917, 54919, 54941, 54949, 54959, 54973, 54979, 54983, - 55001, 55009, 55021, 55049, 55051, 55057, 55061, 55073, 55079, 55103, - 55109, 55117, 55127, 55147, 55163, 55171, 55201, 55207, 55213, 55217, - 55219, 55229, 55243, 55249, 55259, 55291, 55313, 55331, 55333, 55337, - 55339, 55343, 55351, 55373, 55381, 55399, 55411, 55439, 55441, 55457, - 55469, 55487, 55501, 55511, 55529, 55541, 55547, 55579, 55589, 55603, - 55609, 55619, 55621, 55631, 55633, 55639, 55661, 55663, 55667, 55673, - 55681, 55691, 55697, 55711, 55717, 55721, 55733, 55763, 55787, 55793, - 55799, 55807, 55813, 55817, 55819, 55823, 55829, 55837, 55843, 55849, - 55871, 55889, 55897, 55901, 55903, 55921, 55927, 55931, 55933, 55949, - 55967, 55987, 55997, 56003, 56009, 56039, 56041, 56053, 56081, 56087, - 56093, 56099, 56101, 56113, 56123, 56131, 56149, 56167, 56171, 56179, - 56197, 56207, 56209, 56237, 56239, 56249, 56263, 56267, 56269, 56299, - 56311, 56333, 56359, 56369, 56377, 56383, 56393, 56401, 56417, 56431, - 56437, 56443, 56453, 56467, 56473, 56477, 56479, 56489, 56501, 56503, - 56509, 56519, 56527, 56531, 56533, 56543, 56569, 56591, 56597, 56599, - 56611, 56629, 56633, 56659, 56663, 56671, 56681, 56687, 56701, 56711, - 56713, 56731, 56737, 56747, 56767, 56773, 56779, 56783, 56807, 56809, - 56813, 56821, 56827, 56843, 56857, 56873, 56891, 56893, 56897, 56909, - 56911, 56921, 56923, 56929, 56941, 56951, 56957, 56963, 56983, 56989, - 56993, 56999, 57037, 57041, 57047, 57059, 57073, 57077, 57089, 57097, - 57107, 57119, 57131, 57139, 57143, 57149, 57163, 57173, 57179, 57191, - 57193, 57203, 57221, 57223, 57241, 57251, 57259, 57269, 57271, 57283, - 57287, 57301, 57329, 57331, 57347, 57349, 57367, 57373, 57383, 57389, - 57397, 57413, 57427, 57457, 57467, 57487, 57493, 57503, 57527, 57529, - 57557, 57559, 57571, 57587, 57593, 57601, 57637, 57641, 57649, 57653, - 57667, 57679, 57689, 57697, 57709, 57713, 57719, 57727, 57731, 57737, - 57751, 57773, 57781, 57787, 57791, 57793, 57803, 57809, 57829, 57839, - 57847, 57853, 57859, 57881, 57899, 57901, 57917, 57923, 57943, 57947, - 57973, 57977, 57991, 58013, 58027, 58031, 58043, 58049, 58057, 58061, - 58067, 58073, 58099, 58109, 58111, 58129, 58147, 58151, 58153, 58169, - 58171, 58189, 58193, 58199, 58207, 58211, 58217, 58229, 58231, 58237, - 58243, 58271, 58309, 58313, 58321, 58337, 58363, 58367, 58369, 58379, - 58391, 58393, 58403, 58411, 58417, 58427, 58439, 58441, 58451, 58453, - 58477, 58481, 58511, 58537, 58543, 58549, 58567, 58573, 58579, 58601, - 58603, 58613, 58631, 58657, 58661, 58679, 58687, 58693, 58699, 58711, - 58727, 58733, 58741, 58757, 58763, 58771, 58787, 58789, 58831, 58889, - 58897, 58901, 58907, 58909, 58913, 58921, 58937, 58943, 58963, 58967, - 58979, 58991, 58997, 59009, 59011, 59021, 59023, 59029, 59051, 59053, - 59063, 59069, 59077, 59083, 59093, 59107, 59113, 59119, 59123, 59141, - 59149, 59159, 59167, 59183, 59197, 59207, 59209, 59219, 59221, 59233, - 59239, 59243, 59263, 59273, 59281, 59333, 59341, 59351, 59357, 59359, - 59369, 59377, 59387, 59393, 59399, 59407, 59417, 59419, 59441, 59443, - 59447, 59453, 59467, 59471, 59473, 59497, 59509, 59513, 59539, 59557, - 59561, 59567, 59581, 59611, 59617, 59621, 59627, 59629, 59651, 59659, - 59663, 59669, 59671, 59693, 59699, 59707, 59723, 59729, 59743, 59747, - 59753, 59771, 59779, 59791, 59797, 59809, 59833, 59863, 59879, 59887, - 59921, 59929, 59951, 59957, 59971, 59981, 59999, 60013, 60017, 60029, - 60037, 60041, 60077, 60083, 60089, 60091, 60101, 60103, 60107, 60127, - 60133, 60139, 60149, 60161, 60167, 60169, 60209, 60217, 60223, 60251, - 60257, 60259, 60271, 60289, 60293, 60317, 60331, 60337, 60343, 60353, - 60373, 60383, 60397, 60413, 60427, 60443, 60449, 60457, 60493, 60497, - 60509, 60521, 60527, 60539, 60589, 60601, 60607, 60611, 60617, 60623, - 60631, 60637, 60647, 60649, 60659, 60661, 60679, 60689, 60703, 60719, - 60727, 60733, 60737, 60757, 60761, 60763, 60773, 60779, 60793, 60811, - 60821, 60859, 60869, 60887, 60889, 60899, 60901, 60913, 60917, 60919, - 60923, 60937, 60943, 60953, 60961, 61001, 61007, 61027, 61031, 61043, - 61051, 61057, 61091, 61099, 61121, 61129, 61141, 61151, 61153, 61169, - 61211, 61223, 61231, 61253, 61261, 61283, 61291, 61297, 61331, 61333, - 61339, 61343, 61357, 61363, 61379, 61381, 61403, 61409, 61417, 61441, - 61463, 61469, 61471, 61483, 61487, 61493, 61507, 61511, 61519, 61543, - 61547, 61553, 61559, 61561, 61583, 61603, 61609, 61613, 61627, 61631, - 61637, 61643, 61651, 61657, 61667, 61673, 61681, 61687, 61703, 61717, - 61723, 61729, 61751, 61757, 61781, 61813, 61819, 61837, 61843, 61861, - 61871, 61879, 61909, 61927, 61933, 61949, 61961, 61967, 61979, 61981, - 61987, 61991, 62003, 62011, 62017, 62039, 62047, 62053, 62057, 62071, - 62081, 62099, 62119, 62129, 62131, 62137, 62141, 62143, 62171, 62189, - 62191, 62201, 62207, 62213, 62219, 62233, 62273, 62297, 62299, 62303, - 62311, 62323, 62327, 62347, 62351, 62383, 62401, 62417, 62423, 62459, - 62467, 62473, 62477, 62483, 62497, 62501, 62507, 62533, 62539, 62549, - 62563, 62581, 62591, 62597, 62603, 62617, 62627, 62633, 62639, 62653, - 62659, 62683, 62687, 62701, 62723, 62731, 62743, 62753, 62761, 62773, - 62791, 62801, 62819, 62827, 62851, 62861, 62869, 62873, 62897, 62903, - 62921, 62927, 62929, 62939, 62969, 62971, 62981, 62983, 62987, 62989, - 63029, 63031, 63059, 63067, 63073, 63079, 63097, 63103, 63113, 63127, - 63131, 63149, 63179, 63197, 63199, 63211, 63241, 63247, 63277, 63281, - 63299, 63311, 63313, 63317, 63331, 63337, 63347, 63353, 63361, 63367, - 63377, 63389, 63391, 63397, 63409, 63419, 63421, 63439, 63443, 63463, - 63467, 63473, 63487, 63493, 63499, 63521, 63527, 63533, 63541, 63559, - 63577, 63587, 63589, 63599, 63601, 63607, 63611, 63617, 63629, 63647, - 63649, 63659, 63667, 63671, 63689, 63691, 63697, 63703, 63709, 63719, - 63727, 63737, 63743, 63761, 63773, 63781, 63793, 63799, 63803, 63809, - 63823, 63839, 63841, 63853, 63857, 63863, 63901, 63907, 63913, 63929, - 63949, 63977, 63997, 64007, 64013, 64019, 64033, 64037, 64063, 64067, - 64081, 64091, 64109, 64123, 64151, 64153, 64157, 64171, 64187, 64189, - 64217, 64223, 64231, 64237, 64271, 64279, 64283, 64301, 64303, 64319, - 64327, 64333, 64373, 64381, 64399, 64403, 64433, 64439, 64451, 64453, - 64483, 64489, 64499, 64513, 64553, 64567, 64577, 64579, 64591, 64601, - 64609, 64613, 64621, 64627, 64633, 64661, 64663, 64667, 64679, 64693, - 64709, 64717, 64747, 64763, 64781, 64783, 64793, 64811, 64817, 64849, - 64853, 64871, 64877, 64879, 64891, 64901, 64919, 64921, 64927, 64937, - 64951, 64969, 64997, 65003, 65011, 65027, 65029, 65033, 65053, 65063, - 65071, 65089, 65099, 65101, 65111, 65119, 65123, 65129, 65141, 65147, - 65167, 65171, 65173, 65179, 65183, 65203, 65213, 65239, 65257, 65267, - 65269, 65287, 65293, 65309, 65323, 65327, 65353, 65357, 65371, 65381, - 65393, 65407, 65413, 65419, 65423, 65437, 65447, 65449, 65479, 65497, - 65519, 65521, 65537, 65539, 65543, 65551, 65557, 65563, 65579, 65581, - 65587, 65599, 65609, 65617, 65629, 65633, 65647, 65651, 65657, 65677, - 65687, 65699, 65701, 65707, 65713, 65717, 65719, 65729, 65731, 65761, - 65777, 65789, 65809, 65827, 65831, 65837, 65839, 65843, 65851, 65867, - 65881, 65899, 65921, 65927, 65929, 65951, 65957, 65963, 65981, 65983, - 65993, 66029, 66037, 66041, 66047, 66067, 66071, 66083, 66089, 66103, - 66107, 66109, 66137, 66161, 66169, 66173, 66179, 66191, 66221, 66239, - 66271, 66293, 66301, 66337, 66343, 66347, 66359, 66361, 66373, 66377, - 66383, 66403, 66413, 66431, 66449, 66457, 66463, 66467, 66491, 66499, - 66509, 66523, 66529, 66533, 66541, 66553, 66569, 66571, 66587, 66593, - 66601, 66617, 66629, 66643, 66653, 66683, 66697, 66701, 66713, 66721, - 66733, 66739, 66749, 66751, 66763, 66791, 66797, 66809, 66821, 66841, - 66851, 66853, 66863, 66877, 66883, 66889, 66919, 66923, 66931, 66943, - 66947, 66949, 66959, 66973, 66977, 67003, 67021, 67033, 67043, 67049, - 67057, 67061, 67073, 67079, 67103, 67121, 67129, 67139, 67141, 67153, - 67157, 67169, 67181, 67187, 67189, 67211, 67213, 67217, 67219, 67231, - 67247, 67261, 67271, 67273, 67289, 67307, 67339, 67343, 67349, 67369, - 67391, 67399, 67409, 67411, 67421, 67427, 67429, 67433, 67447, 67453, - 67477, 67481, 67489, 67493, 67499, 67511, 67523, 67531, 67537, 67547, - 67559, 67567, 67577, 67579, 67589, 67601, 67607, 67619, 67631, 67651, - 67679, 67699, 67709, 67723, 67733, 67741, 67751, 67757, 67759, 67763, - 67777, 67783, 67789, 67801, 67807, 67819, 67829, 67843, 67853, 67867, - 67883, 67891, 67901, 67927, 67931, 67933, 67939, 67943, 67957, 67961, - 67967, 67979, 67987, 67993, 68023, 68041, 68053, 68059, 68071, 68087, - 68099, 68111, 68113, 68141, 68147, 68161, 68171, 68207, 68209, 68213, - 68219, 68227, 68239, 68261, 68279, 68281, 68311, 68329, 68351, 68371, - 68389, 68399, 68437, 68443, 68447, 68449, 68473, 68477, 68483, 68489, - 68491, 68501, 68507, 68521, 68531, 68539, 68543, 68567, 68581, 68597, - 68611, 68633, 68639, 68659, 68669, 68683, 68687, 68699, 68711, 68713, - 68729, 68737, 68743, 68749, 68767, 68771, 68777, 68791, 68813, 68819, - 68821, 68863, 68879, 68881, 68891, 68897, 68899, 68903, 68909, 68917, - 68927, 68947, 68963, 68993, 69001, 69011, 69019, 69029, 69031, 69061, - 69067, 69073, 69109, 69119, 69127, 69143, 69149, 69151, 69163, 69191, - 69193, 69197, 69203, 69221, 69233, 69239, 69247, 69257, 69259, 69263, - 69313, 69317, 69337, 69341, 69371, 69379, 69383, 69389, 69401, 69403, - 69427, 69431, 69439, 69457, 69463, 69467, 69473, 69481, 69491, 69493, - 69497, 69499, 69539, 69557, 69593, 69623, 69653, 69661, 69677, 69691, - 69697, 69709, 69737, 69739, 69761, 69763, 69767, 69779, 69809, 69821, - 69827, 69829, 69833, 69847, 69857, 69859, 69877, 69899, 69911, 69929, - 69931, 69941, 69959, 69991, 69997, 70001, 70003, 70009, 70019, 70039, - 70051, 70061, 70067, 70079, 70099, 70111, 70117, 70121, 70123, 70139, - 70141, 70157, 70163, 70177, 70181, 70183, 70199, 70201, 70207, 70223, - 70229, 70237, 70241, 70249, 70271, 70289, 70297, 70309, 70313, 70321, - 70327, 70351, 70373, 70379, 70381, 70393, 70423, 70429, 70439, 70451, - 70457, 70459, 70481, 70487, 70489, 70501, 70507, 70529, 70537, 70549, - 70571, 70573, 70583, 70589, 70607, 70619, 70621, 70627, 70639, 70657, - 70663, 70667, 70687, 70709, 70717, 70729, 70753, 70769, 70783, 70793, - 70823, 70841, 70843, 70849, 70853, 70867, 70877, 70879, 70891, 70901, - 70913, 70919, 70921, 70937, 70949, 70951, 70957, 70969, 70979, 70981, - 70991, 70997, 70999, 71011, 71023, 71039, 71059, 71069, 71081, 71089, - 71119, 71129, 71143, 71147, 71153, 71161, 71167, 71171, 71191, 71209, - 71233, 71237, 71249, 71257, 71261, 71263, 71287, 71293, 71317, 71327, - 71329, 71333, 71339, 71341, 71347, 71353, 71359, 71363, 71387, 71389, - 71399, 71411, 71413, 71419, 71429, 71437, 71443, 71453, 71471, 71473, - 71479, 71483, 71503, 71527, 71537, 71549, 71551, 71563, 71569, 71593, - 71597, 71633, 71647, 71663, 71671, 71693, 71699, 71707, 71711, 71713, - 71719, 71741, 71761, 71777, 71789, 71807, 71809, 71821, 71837, 71843, - 71849, 71861, 71867, 71879, 71881, 71887, 71899, 71909, 71917, 71933, - 71941, 71947, 71963, 71971, 71983, 71987, 71993, 71999, 72019, 72031, - 72043, 72047, 72053, 72073, 72077, 72089, 72091, 72101, 72103, 72109, - 72139, 72161, 72167, 72169, 72173, 72211, 72221, 72223, 72227, 72229, - 72251, 72253, 72269, 72271, 72277, 72287, 72307, 72313, 72337, 72341, - 72353, 72367, 72379, 72383, 72421, 72431, 72461, 72467, 72469, 72481, - 72493, 72497, 72503, 72533, 72547, 72551, 72559, 72577, 72613, 72617, - 72623, 72643, 72647, 72649, 72661, 72671, 72673, 72679, 72689, 72701, - 72707, 72719, 72727, 72733, 72739, 72763, 72767, 72797, 72817, 72823, - 72859, 72869, 72871, 72883, 72889, 72893, 72901, 72907, 72911, 72923, - 72931, 72937, 72949, 72953, 72959, 72973, 72977, 72997, 73009, 73013, - 73019, 73037, 73039, 73043, 73061, 73063, 73079, 73091, 73121, 73127, - 73133, 73141, 73181, 73189, 73237, 73243, 73259, 73277, 73291, 73303, - 73309, 73327, 73331, 73351, 73361, 73363, 73369, 73379, 73387, 73417, - 73421, 73433, 73453, 73459, 73471, 73477, 73483, 73517, 73523, 73529, - 73547, 73553, 73561, 73571, 73583, 73589, 73597, 73607, 73609, 73613, - 73637, 73643, 73651, 73673, 73679, 73681, 73693, 73699, 73709, 73721, - 73727, 73751, 73757, 73771, 73783, 73819, 73823, 73847, 73849, 73859, - 73867, 73877, 73883, 73897, 73907, 73939, 73943, 73951, 73961, 73973, - 73999, 74017, 74021, 74027, 74047, 74051, 74071, 74077, 74093, 74099, - 74101, 74131, 74143, 74149, 74159, 74161, 74167, 74177, 74189, 74197, - 74201, 74203, 74209, 74219, 74231, 74257, 74279, 74287, 74293, 74297, - 74311, 74317, 74323, 74353, 74357, 74363, 74377, 74381, 74383, 74411, - 74413, 74419, 74441, 74449, 74453, 74471, 74489, 74507, 74509, 74521, - 74527, 74531, 74551, 74561, 74567, 74573, 74587, 74597, 74609, 74611, - 74623, 74653, 74687, 74699, 74707, 74713, 74717, 74719, 74729, 74731, - 74747, 74759, 74761, 74771, 74779, 74797, 74821, 74827, 74831, 74843, - 74857, 74861, 74869, 74873, 74887, 74891, 74897, 74903, 74923, 74929, - 74933, 74941, 74959, 75011, 75013, 75017, 75029, 75037, 75041, 75079, - 75083, 75109, 75133, 75149, 75161, 75167, 75169, 75181, 75193, 75209, - 75211, 75217, 75223, 75227, 75239, 75253, 75269, 75277, 75289, 75307, - 75323, 75329, 75337, 75347, 75353, 75367, 75377, 75389, 75391, 75401, - 75403, 75407, 75431, 75437, 75479, 75503, 75511, 75521, 75527, 75533, - 75539, 75541, 75553, 75557, 75571, 75577, 75583, 75611, 75617, 75619, - 75629, 75641, 75653, 75659, 75679, 75683, 75689, 75703, 75707, 75709, - 75721, 75731, 75743, 75767, 75773, 75781, 75787, 75793, 75797, 75821, - 75833, 75853, 75869, 75883, 75913, 75931, 75937, 75941, 75967, 75979, - 75983, 75989, 75991, 75997, 76001, 76003, 76031, 76039, 76079, 76081, - 76091, 76099, 76103, 76123, 76129, 76147, 76157, 76159, 76163, 76207, - 76213, 76231, 76243, 76249, 76253, 76259, 76261, 76283, 76289, 76303, - 76333, 76343, 76367, 76369, 76379, 76387, 76403, 76421, 76423, 76441, - 76463, 76471, 76481, 76487, 76493, 76507, 76511, 76519, 76537, 76541, - 76543, 76561, 76579, 76597, 76603, 76607, 76631, 76649, 76651, 76667, - 76673, 76679, 76697, 76717, 76733, 76753, 76757, 76771, 76777, 76781, - 76801, 76819, 76829, 76831, 76837, 76847, 76871, 76873, 76883, 76907, - 76913, 76919, 76943, 76949, 76961, 76963, 76991, 77003, 77017, 77023, - 77029, 77041, 77047, 77069, 77081, 77093, 77101, 77137, 77141, 77153, - 77167, 77171, 77191, 77201, 77213, 77237, 77239, 77243, 77249, 77261, - 77263, 77267, 77269, 77279, 77291, 77317, 77323, 77339, 77347, 77351, - 77359, 77369, 77377, 77383, 77417, 77419, 77431, 77447, 77471, 77477, - 77479, 77489, 77491, 77509, 77513, 77521, 77527, 77543, 77549, 77551, - 77557, 77563, 77569, 77573, 77587, 77591, 77611, 77617, 77621, 77641, - 77647, 77659, 77681, 77687, 77689, 77699, 77711, 77713, 77719, 77723, - 77731, 77743, 77747, 77761, 77773, 77783, 77797, 77801, 77813, 77839, - 77849, 77863, 77867, 77893, 77899, 77929, 77933, 77951, 77969, 77977, - 77983, 77999, 78007, 78017, 78031, 78041, 78049, 78059, 78079, 78101, - 78121, 78137, 78139, 78157, 78163, 78167, 78173, 78179, 78191, 78193, - 78203, 78229, 78233, 78241, 78259, 78277, 78283, 78301, 78307, 78311, - 78317, 78341, 78347, 78367, 78401, 78427, 78437, 78439, 78467, 78479, - 78487, 78497, 78509, 78511, 78517, 78539, 78541, 78553, 78569, 78571, - 78577, 78583, 78593, 78607, 78623, 78643, 78649, 78653, 78691, 78697, - 78707, 78713, 78721, 78737, 78779, 78781, 78787, 78791, 78797, 78803, - 78809, 78823, 78839, 78853, 78857, 78877, 78887, 78889, 78893, 78901, - 78919, 78929, 78941, 78977, 78979, 78989, 79031, 79039, 79043, 79063, - 79087, 79103, 79111, 79133, 79139, 79147, 79151, 79153, 79159, 79181, - 79187, 79193, 79201, 79229, 79231, 79241, 79259, 79273, 79279, 79283, - 79301, 79309, 79319, 79333, 79337, 79349, 79357, 79367, 79379, 79393, - 79397, 79399, 79411, 79423, 79427, 79433, 79451, 79481, 79493, 79531, - 79537, 79549, 79559, 79561, 79579, 79589, 79601, 79609, 79613, 79621, - 79627, 79631, 79633, 79657, 79669, 79687, 79691, 79693, 79697, 79699, - 79757, 79769, 79777, 79801, 79811, 79813, 79817, 79823, 79829, 79841, - 79843, 79847, 79861, 79867, 79873, 79889, 79901, 79903, 79907, 79939, - 79943, 79967, 79973, 79979, 79987, 79997, 79999, 80021, 80039, 80051, - 80071, 80077, 80107, 80111, 80141, 80147, 80149, 80153, 80167, 80173, - 80177, 80191, 80207, 80209, 80221, 80231, 80233, 80239, 80251, 80263, - 80273, 80279, 80287, 80309, 80317, 80329, 80341, 80347, 80363, 80369, - 80387, 80407, 80429, 80447, 80449, 80471, 80473, 80489, 80491, 80513, - 80527, 80537, 80557, 80567, 80599, 80603, 80611, 80621, 80627, 80629, - 80651, 80657, 80669, 80671, 80677, 80681, 80683, 80687, 80701, 80713, - 80737, 80747, 80749, 80761, 80777, 80779, 80783, 80789, 80803, 80809, - 80819, 80831, 80833, 80849, 80863, 80897, 80909, 80911, 80917, 80923, - 80929, 80933, 80953, 80963, 80989, 81001, 81013, 81017, 81019, 81023, - 81031, 81041, 81043, 81047, 81049, 81071, 81077, 81083, 81097, 81101, - 81119, 81131, 81157, 81163, 81173, 81181, 81197, 81199, 81203, 81223, - 81233, 81239, 81281, 81283, 81293, 81299, 81307, 81331, 81343, 81349, - 81353, 81359, 81371, 81373, 81401, 81409, 81421, 81439, 81457, 81463, - 81509, 81517, 81527, 81533, 81547, 81551, 81553, 81559, 81563, 81569, - 81611, 81619, 81629, 81637, 81647, 81649, 81667, 81671, 81677, 81689, - 81701, 81703, 81707, 81727, 81737, 81749, 81761, 81769, 81773, 81799, - 81817, 81839, 81847, 81853, 81869, 81883, 81899, 81901, 81919, 81929, - 81931, 81937, 81943, 81953, 81967, 81971, 81973, 82003, 82007, 82009, - 82013, 82021, 82031, 82037, 82039, 82051, 82067, 82073, 82129, 82139, - 82141, 82153, 82163, 82171, 82183, 82189, 82193, 82207, 82217, 82219, - 82223, 82231, 82237, 82241, 82261, 82267, 82279, 82301, 82307, 82339, - 82349, 82351, 82361, 82373, 82387, 82393, 82421, 82457, 82463, 82469, - 82471, 82483, 82487, 82493, 82499, 82507, 82529, 82531, 82549, 82559, - 82561, 82567, 82571, 82591, 82601, 82609, 82613, 82619, 82633, 82651, - 82657, 82699, 82721, 82723, 82727, 82729, 82757, 82759, 82763, 82781, - 82787, 82793, 82799, 82811, 82813, 82837, 82847, 82883, 82889, 82891, - 82903, 82913, 82939, 82963, 82981, 82997, 83003, 83009, 83023, 83047, - 83059, 83063, 83071, 83077, 83089, 83093, 83101, 83117, 83137, 83177, - 83203, 83207, 83219, 83221, 83227, 83231, 83233, 83243, 83257, 83267, - 83269, 83273, 83299, 83311, 83339, 83341, 83357, 83383, 83389, 83399, - 83401, 83407, 83417, 83423, 83431, 83437, 83443, 83449, 83459, 83471, - 83477, 83497, 83537, 83557, 83561, 83563, 83579, 83591, 83597, 83609, - 83617, 83621, 83639, 83641, 83653, 83663, 83689, 83701, 83717, 83719, - 83737, 83761, 83773, 83777, 83791, 83813, 83833, 83843, 83857, 83869, - 83873, 83891, 83903, 83911, 83921, 83933, 83939, 83969, 83983, 83987, - 84011, 84017, 84047, 84053, 84059, 84061, 84067, 84089, 84121, 84127, - 84131, 84137, 84143, 84163, 84179, 84181, 84191, 84199, 84211, 84221, - 84223, 84229, 84239, 84247, 84263, 84299, 84307, 84313, 84317, 84319, - 84347, 84349, 84377, 84389, 84391, 84401, 84407, 84421, 84431, 84437, - 84443, 84449, 84457, 84463, 84467, 84481, 84499, 84503, 84509, 84521, - 84523, 84533, 84551, 84559, 84589, 84629, 84631, 84649, 84653, 84659, - 84673, 84691, 84697, 84701, 84713, 84719, 84731, 84737, 84751, 84761, - 84787, 84793, 84809, 84811, 84827, 84857, 84859, 84869, 84871, 84913, - 84919, 84947, 84961, 84967, 84977, 84979, 84991, 85009, 85021, 85027, - 85037, 85049, 85061, 85081, 85087, 85091, 85093, 85103, 85109, 85121, - 85133, 85147, 85159, 85193, 85199, 85201, 85213, 85223, 85229, 85237, - 85243, 85247, 85259, 85297, 85303, 85313, 85331, 85333, 85361, 85363, - 85369, 85381, 85411, 85427, 85429, 85439, 85447, 85451, 85453, 85469, - 85487, 85513, 85517, 85523, 85531, 85549, 85571, 85577, 85597, 85601, - 85607, 85619, 85621, 85627, 85639, 85643, 85661, 85667, 85669, 85691, - 85703, 85711, 85717, 85733, 85751, 85781, 85793, 85817, 85819, 85829, - 85831, 85837, 85843, 85847, 85853, 85889, 85903, 85909, 85931, 85933, - 85991, 85999, 86011, 86017, 86027, 86029, 86069, 86077, 86083, 86111, - 86113, 86117, 86131, 86137, 86143, 86161, 86171, 86179, 86183, 86197, - 86201, 86209, 86239, 86243, 86249, 86257, 86263, 86269, 86287, 86291, - 86293, 86297, 86311, 86323, 86341, 86351, 86353, 86357, 86369, 86371, - 86381, 86389, 86399, 86413, 86423, 86441, 86453, 86461, 86467, 86477, - 86491, 86501, 86509, 86531, 86533, 86539, 86561, 86573, 86579, 86587, - 86599, 86627, 86629, 86677, 86689, 86693, 86711, 86719, 86729, 86743, - 86753, 86767, 86771, 86783, 86813, 86837, 86843, 86851, 86857, 86861, - 86869, 86923, 86927, 86929, 86939, 86951, 86959, 86969, 86981, 86993, - 87011, 87013, 87037, 87041, 87049, 87071, 87083, 87103, 87107, 87119, - 87121, 87133, 87149, 87151, 87179, 87181, 87187, 87211, 87221, 87223, - 87251, 87253, 87257, 87277, 87281, 87293, 87299, 87313, 87317, 87323, - 87337, 87359, 87383, 87403, 87407, 87421, 87427, 87433, 87443, 87473, - 87481, 87491, 87509, 87511, 87517, 87523, 87539, 87541, 87547, 87553, - 87557, 87559, 87583, 87587, 87589, 87613, 87623, 87629, 87631, 87641, - 87643, 87649, 87671, 87679, 87683, 87691, 87697, 87701, 87719, 87721, - 87739, 87743, 87751, 87767, 87793, 87797, 87803, 87811, 87833, 87853, - 87869, 87877, 87881, 87887, 87911, 87917, 87931, 87943, 87959, 87961, - 87973, 87977, 87991, 88001, 88003, 88007, 88019, 88037, 88069, 88079, - 88093, 88117, 88129, 88169, 88177, 88211, 88223, 88237, 88241, 88259, - 88261, 88289, 88301, 88321, 88327, 88337, 88339, 88379, 88397, 88411, - 88423, 88427, 88463, 88469, 88471, 88493, 88499, 88513, 88523, 88547, - 88589, 88591, 88607, 88609, 88643, 88651, 88657, 88661, 88663, 88667, - 88681, 88721, 88729, 88741, 88747, 88771, 88789, 88793, 88799, 88801, - 88807, 88811, 88813, 88817, 88819, 88843, 88853, 88861, 88867, 88873, - 88883, 88897, 88903, 88919, 88937, 88951, 88969, 88993, 88997, 89003, - 89009, 89017, 89021, 89041, 89051, 89057, 89069, 89071, 89083, 89087, - 89101, 89107, 89113, 89119, 89123, 89137, 89153, 89189, 89203, 89209, - 89213, 89227, 89231, 89237, 89261, 89269, 89273, 89293, 89303, 89317, - 89329, 89363, 89371, 89381, 89387, 89393, 89399, 89413, 89417, 89431, - 89443, 89449, 89459, 89477, 89491, 89501, 89513, 89519, 89521, 89527, - 89533, 89561, 89563, 89567, 89591, 89597, 89599, 89603, 89611, 89627, - 89633, 89653, 89657, 89659, 89669, 89671, 89681, 89689, 89753, 89759, - 89767, 89779, 89783, 89797, 89809, 89819, 89821, 89833, 89839, 89849, - 89867, 89891, 89897, 89899, 89909, 89917, 89923, 89939, 89959, 89963, - 89977, 89983, 89989, 90001, 90007, 90011, 90017, 90019, 90023, 90031, - 90053, 90059, 90067, 90071, 90073, 90089, 90107, 90121, 90127, 90149, - 90163, 90173, 90187, 90191, 90197, 90199, 90203, 90217, 90227, 90239, - 90247, 90263, 90271, 90281, 90289, 90313, 90353, 90359, 90371, 90373, - 90379, 90397, 90401, 90403, 90407, 90437, 90439, 90469, 90473, 90481, - 90499, 90511, 90523, 90527, 90529, 90533, 90547, 90583, 90599, 90617, - 90619, 90631, 90641, 90647, 90659, 90677, 90679, 90697, 90703, 90709, - 90731, 90749, 90787, 90793, 90803, 90821, 90823, 90833, 90841, 90847, - 90863, 90887, 90901, 90907, 90911, 90917, 90931, 90947, 90971, 90977, - 90989, 90997, 91009, 91019, 91033, 91079, 91081, 91097, 91099, 91121, - 91127, 91129, 91139, 91141, 91151, 91153, 91159, 91163, 91183, 91193, - 91199, 91229, 91237, 91243, 91249, 91253, 91283, 91291, 91297, 91303, - 91309, 91331, 91367, 91369, 91373, 91381, 91387, 91393, 91397, 91411, - 91423, 91433, 91453, 91457, 91459, 91463, 91493, 91499, 91513, 91529, - 91541, 91571, 91573, 91577, 91583, 91591, 91621, 91631, 91639, 91673, - 91691, 91703, 91711, 91733, 91753, 91757, 91771, 91781, 91801, 91807, - 91811, 91813, 91823, 91837, 91841, 91867, 91873, 91909, 91921, 91939, - 91943, 91951, 91957, 91961, 91967, 91969, 91997, 92003, 92009, 92033, - 92041, 92051, 92077, 92083, 92107, 92111, 92119, 92143, 92153, 92173, - 92177, 92179, 92189, 92203, 92219, 92221, 92227, 92233, 92237, 92243, - 92251, 92269, 92297, 92311, 92317, 92333, 92347, 92353, 92357, 92363, - 92369, 92377, 92381, 92383, 92387, 92399, 92401, 92413, 92419, 92431, - 92459, 92461, 92467, 92479, 92489, 92503, 92507, 92551, 92557, 92567, - 92569, 92581, 92593, 92623, 92627, 92639, 92641, 92647, 92657, 92669, - 92671, 92681, 92683, 92693, 92699, 92707, 92717, 92723, 92737, 92753, - 92761, 92767, 92779, 92789, 92791, 92801, 92809, 92821, 92831, 92849, - 92857, 92861, 92863, 92867, 92893, 92899, 92921, 92927, 92941, 92951, - 92957, 92959, 92987, 92993, 93001, 93047, 93053, 93059, 93077, 93083, - 93089, 93097, 93103, 93113, 93131, 93133, 93139, 93151, 93169, 93179, - 93187, 93199, 93229, 93239, 93241, 93251, 93253, 93257, 93263, 93281, - 93283, 93287, 93307, 93319, 93323, 93329, 93337, 93371, 93377, 93383, - 93407, 93419, 93427, 93463, 93479, 93481, 93487, 93491, 93493, 93497, - 93503, 93523, 93529, 93553, 93557, 93559, 93563, 93581, 93601, 93607, - 93629, 93637, 93683, 93701, 93703, 93719, 93739, 93761, 93763, 93787, - 93809, 93811, 93827, 93851, 93871, 93887, 93889, 93893, 93901, 93911, - 93913, 93923, 93937, 93941, 93949, 93967, 93971, 93979, 93983, 93997, - 94007, 94009, 94033, 94049, 94057, 94063, 94079, 94099, 94109, 94111, - 94117, 94121, 94151, 94153, 94169, 94201, 94207, 94219, 94229, 94253, - 94261, 94273, 94291, 94307, 94309, 94321, 94327, 94331, 94343, 94349, - 94351, 94379, 94397, 94399, 94421, 94427, 94433, 94439, 94441, 94447, - 94463, 94477, 94483, 94513, 94529, 94531, 94541, 94543, 94547, 94559, - 94561, 94573, 94583, 94597, 94603, 94613, 94621, 94649, 94651, 94687, - 94693, 94709, 94723, 94727, 94747, 94771, 94777, 94781, 94789, 94793, - 94811, 94819, 94823, 94837, 94841, 94847, 94849, 94873, 94889, 94903, - 94907, 94933, 94949, 94951, 94961, 94993, 94999, 95003, 95009, 95021, - 95027, 95063, 95071, 95083, 95087, 95089, 95093, 95101, 95107, 95111, - 95131, 95143, 95153, 95177, 95189, 95191, 95203, 95213, 95219, 95231, - 95233, 95239, 95257, 95261, 95267, 95273, 95279, 95287, 95311, 95317, - 95327, 95339, 95369, 95383, 95393, 95401, 95413, 95419, 95429, 95441, - 95443, 95461, 95467, 95471, 95479, 95483, 95507, 95527, 95531, 95539, - 95549, 95561, 95569, 95581, 95597, 95603, 95617, 95621, 95629, 95633, - 95651, 95701, 95707, 95713, 95717, 95723, 95731, 95737, 95747, 95773, - 95783, 95789, 95791, 95801, 95803, 95813, 95819, 95857, 95869, 95873, - 95881, 95891, 95911, 95917, 95923, 95929, 95947, 95957, 95959, 95971, - 95987, 95989, 96001, 96013, 96017, 96043, 96053, 96059, 96079, 96097, - 96137, 96149, 96157, 96167, 96179, 96181, 96199, 96211, 96221, 96223, - 96233, 96259, 96263, 96269, 96281, 96289, 96293, 96323, 96329, 96331, - 96337, 96353, 96377, 96401, 96419, 96431, 96443, 96451, 96457, 96461, - 96469, 96479, 96487, 96493, 96497, 96517, 96527, 96553, 96557, 96581, - 96587, 96589, 96601, 96643, 96661, 96667, 96671, 96697, 96703, 96731, - 96737, 96739, 96749, 96757, 96763, 96769, 96779, 96787, 96797, 96799, - 96821, 96823, 96827, 96847, 96851, 96857, 96893, 96907, 96911, 96931, - 96953, 96959, 96973, 96979, 96989, 96997, 97001, 97003, 97007, 97021, - 97039, 97073, 97081, 97103, 97117, 97127, 97151, 97157, 97159, 97169, - 97171, 97177, 97187, 97213, 97231, 97241, 97259, 97283, 97301, 97303, - 97327, 97367, 97369, 97373, 97379, 97381, 97387, 97397, 97423, 97429, - 97441, 97453, 97459, 97463, 97499, 97501, 97511, 97523, 97547, 97549, - 97553, 97561, 97571, 97577, 97579, 97583, 97607, 97609, 97613, 97649, - 97651, 97673, 97687, 97711, 97729, 97771, 97777, 97787, 97789, 97813, - 97829, 97841, 97843, 97847, 97849, 97859, 97861, 97871, 97879, 97883, - 97919, 97927, 97931, 97943, 97961, 97967, 97973, 97987, 98009, 98011, - 98017, 98041, 98047, 98057, 98081, 98101, 98123, 98129, 98143, 98179, - 98207, 98213, 98221, 98227, 98251, 98257, 98269, 98297, 98299, 98317, - 98321, 98323, 98327, 98347, 98369, 98377, 98387, 98389, 98407, 98411, - 98419, 98429, 98443, 98453, 98459, 98467, 98473, 98479, 98491, 98507, - 98519, 98533, 98543, 98561, 98563, 98573, 98597, 98621, 98627, 98639, - 98641, 98663, 98669, 98689, 98711, 98713, 98717, 98729, 98731, 98737, - 98773, 98779, 98801, 98807, 98809, 98837, 98849, 98867, 98869, 98873, - 98887, 98893, 98897, 98899, 98909, 98911, 98927, 98929, 98939, 98947, - 98953, 98963, 98981, 98993, 98999, 99013, 99017, 99023, 99041, 99053, - 99079, 99083, 99089, 99103, 99109, 99119, 99131, 99133, 99137, 99139, - 99149, 99173, 99181, 99191, 99223, 99233, 99241, 99251, 99257, 99259, - 99277, 99289, 99317, 99347, 99349, 99367, 99371, 99377, 99391, 99397, - 99401, 99409, 99431, 99439, 99469, 99487, 99497, 99523, 99527, 99529, - 99551, 99559, 99563, 99571, 99577, 99581, 99607, 99611, 99623, 99643, - 99661, 99667, 99679, 99689, 99707, 99709, 99713, 99719, 99721, 99733, - 99761, 99767, 99787, 99793, 99809, 99817, 99823, 99829, 99833, 99839, - 99859, 99871, 99877, 99881, 99901, 99907, 99923, 99929, 99961, 99971, - 99989, 99991, 100003, 100019, 100043, 100049, 100057, 100069, 100103, 100109, -100129, 100151, 100153, 100169, 100183, 100189, 100193, 100207, 100213, 100237, -100267, 100271, 100279, 100291, 100297, 100313, 100333, 100343, 100357, 100361, -100363, 100379, 100391, 100393, 100403, 100411, 100417, 100447, 100459, 100469, -100483, 100493, 100501, 100511, 100517, 100519, 100523, 100537, 100547, 100549, -100559, 100591, 100609, 100613, 100621, 100649, 100669, 100673, 100693, 100699, -100703, 100733, 100741, 100747, 100769, 100787, 100799, 100801, 100811, 100823, -100829, 100847, 100853, 100907, 100913, 100927, 100931, 100937, 100943, 100957, -100981, 100987, 100999, 101009, 101021, 101027, 101051, 101063, 101081, 101089, -101107, 101111, 101113, 101117, 101119, 101141, 101149, 101159, 101161, 101173, -101183, 101197, 101203, 101207, 101209, 101221, 101267, 101273, 101279, 101281, -101287, 101293, 101323, 101333, 101341, 101347, 101359, 101363, 101377, 101383, -101399, 101411, 101419, 101429, 101449, 101467, 101477, 101483, 101489, 101501, -101503, 101513, 101527, 101531, 101533, 101537, 101561, 101573, 101581, 101599, -101603, 101611, 101627, 101641, 101653, 101663, 101681, 101693, 101701, 101719, -101723, 101737, 101741, 101747, 101749, 101771, 101789, 101797, 101807, 101833, -101837, 101839, 101863, 101869, 101873, 101879, 101891, 101917, 101921, 101929, -101939, 101957, 101963, 101977, 101987, 101999, 102001, 102013, 102019, 102023, -102031, 102043, 102059, 102061, 102071, 102077, 102079, 102101, 102103, 102107, -102121, 102139, 102149, 102161, 102181, 102191, 102197, 102199, 102203, 102217, -102229, 102233, 102241, 102251, 102253, 102259, 102293, 102299, 102301, 102317, -102329, 102337, 102359, 102367, 102397, 102407, 102409, 102433, 102437, 102451, -102461, 102481, 102497, 102499, 102503, 102523, 102533, 102539, 102547, 102551, -102559, 102563, 102587, 102593, 102607, 102611, 102643, 102647, 102653, 102667, -102673, 102677, 102679, 102701, 102761, 102763, 102769, 102793, 102797, 102811, -102829, 102841, 102859, 102871, 102877, 102881, 102911, 102913, 102929, 102931, -102953, 102967, 102983, 103001, 103007, 103043, 103049, 103067, 103069, 103079, -103087, 103091, 103093, 103099, 103123, 103141, 103171, 103177, 103183, 103217, -103231, 103237, 103289, 103291, 103307, 103319, 103333, 103349, 103357, 103387, -103391, 103393, 103399, 103409, 103421, 103423, 103451, 103457, 103471, 103483, -103511, 103529, 103549, 103553, 103561, 103567, 103573, 103577, 103583, 103591, -103613, 103619, 103643, 103651, 103657, 103669, 103681, 103687, 103699, 103703, -103723, 103769, 103787, 103801, 103811, 103813, 103837, 103841, 103843, 103867, -103889, 103903, 103913, 103919, 103951, 103963, 103967, 103969, 103979, 103981, -103991, 103993, 103997, 104003, 104009, 104021, 104033, 104047, 104053, 104059, -104087, 104089, 104107, 104113, 104119, 104123, 104147, 104149, 104161, 104173, -104179, 104183, 104207, 104231, 104233, 104239, 104243, 104281, 104287, 104297, -104309, 104311, 104323, 104327, 104347, 104369, 104381, 104383, 104393, 104399, -104417, 104459, 104471, 104473, 104479, 104491, 104513, 104527, 104537, 104543, -104549, 104551, 104561, 104579, 104593, 104597, 104623, 104639, 104651, 104659, -104677, 104681, 104683, 104693, 104701, 104707, 104711, 104717, 104723, 104729, -) diff --git a/resources/lib/deps/Cryptodome/Util/number.pyi b/resources/lib/deps/Cryptodome/Util/number.pyi deleted file mode 100644 index f8680bf..0000000 --- a/resources/lib/deps/Cryptodome/Util/number.pyi +++ /dev/null @@ -1,19 +0,0 @@ -from typing import List, Optional, Callable - - -def ceil_div(n: int, d: int) -> int: ... -def size (N: int) -> int: ... -def getRandomInteger(N: int, randfunc: Optional[Callable]=None) -> int: ... -def getRandomRange(a: int, b: int, randfunc: Optional[Callable]=None) -> int: ... -def getRandomNBitInteger(N: int, randfunc: Optional[Callable]=None) -> int: ... -def GCD(x: int,y: int) -> int: ... -def inverse(u: int, v: int) -> int: ... -def getPrime(N: int, randfunc: Optional[Callable]=None) -> int: ... -def getStrongPrime(N: int, e: Optional[int]=0, false_positive_prob: Optional[float]=1e-6, randfunc: Optional[Callable]=None) -> int: ... -def isPrime(N: int, false_positive_prob: Optional[float]=1e-6, randfunc: Optional[Callable]=None) -> bool: ... -def long_to_bytes(n: int, blocksize: Optional[int]=0) -> bytes: ... -def bytes_to_long(s: bytes) -> int: ... -def long2str(n: int, blocksize: Optional[int]=0) -> bytes: ... -def str2long(s: bytes) -> int: ... - -sieve_base: List[int] diff --git a/resources/lib/deps/Cryptodome/Util/py3compat.py b/resources/lib/deps/Cryptodome/Util/py3compat.py deleted file mode 100644 index 3294b66..0000000 --- a/resources/lib/deps/Cryptodome/Util/py3compat.py +++ /dev/null @@ -1,185 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Util/py3compat.py : Compatibility code for handling Py3k / Python 2.x -# -# Written in 2010 by Thorsten Behrens -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Compatibility code for handling string/bytes changes from Python 2.x to Py3k - -In Python 2.x, strings (of type ''str'') contain binary data, including encoded -Unicode text (e.g. UTF-8). The separate type ''unicode'' holds Unicode text. -Unicode literals are specified via the u'...' prefix. Indexing or slicing -either type always produces a string of the same type as the original. -Data read from a file is always of '''str'' type. - -In Python 3.x, strings (type ''str'') may only contain Unicode text. The u'...' -prefix and the ''unicode'' type are now redundant. A new type (called -''bytes'') has to be used for binary data (including any particular -''encoding'' of a string). The b'...' prefix allows one to specify a binary -literal. Indexing or slicing a string produces another string. Slicing a byte -string produces another byte string, but the indexing operation produces an -integer. Data read from a file is of '''str'' type if the file was opened in -text mode, or of ''bytes'' type otherwise. - -Since PyCryptodome aims at supporting both Python 2.x and 3.x, the following helper -functions are used to keep the rest of the library as independent as possible -from the actual Python version. - -In general, the code should always deal with binary strings, and use integers -instead of 1-byte character strings. - -b(s) - Take a text string literal (with no prefix or with u'...' prefix) and - make a byte string. -bchr(c) - Take an integer and make a 1-character byte string. -bord(c) - Take the result of indexing on a byte string and make an integer. -tobytes(s) - Take a text string, a byte string, or a sequence of character taken from - a byte string and make a byte string. -""" - -import sys -import abc - - -if sys.version_info[0] == 2: - def b(s): - return s - def bchr(s): - return chr(s) - def bstr(s): - return str(s) - def bord(s): - return ord(s) - def tobytes(s, encoding="latin-1"): - if isinstance(s, unicode): - return s.encode(encoding) - elif isinstance(s, str): - return s - elif isinstance(s, bytearray): - return bytes(s) - elif isinstance(s, memoryview): - return s.tobytes() - else: - return ''.join(s) - def tostr(bs): - return bs - def byte_string(s): - return isinstance(s, str) - - # In Python 2, a memoryview does not support concatenation - def concat_buffers(a, b): - if isinstance(a, memoryview): - a = a.tobytes() - if isinstance(b, memoryview): - b = b.tobytes() - return a + b - - from StringIO import StringIO - BytesIO = StringIO - - from sys import maxint - - iter_range = xrange - - def is_native_int(x): - return isinstance(x, (int, long)) - - def is_string(x): - return isinstance(x, basestring) - - def is_bytes(x): - return isinstance(x, str) or \ - isinstance(x, bytearray) or \ - isinstance(x, memoryview) - - ABC = abc.ABCMeta('ABC', (object,), {'__slots__': ()}) - - FileNotFoundError = IOError - -else: - def b(s): - return s.encode("latin-1") # utf-8 would cause some side-effects we don't want - def bchr(s): - return bytes([s]) - def bstr(s): - if isinstance(s,str): - return bytes(s,"latin-1") - else: - return bytes(s) - def bord(s): - return s - def tobytes(s, encoding="latin-1"): - if isinstance(s, bytes): - return s - elif isinstance(s, bytearray): - return bytes(s) - elif isinstance(s,str): - return s.encode(encoding) - elif isinstance(s, memoryview): - return s.tobytes() - else: - return bytes([s]) - def tostr(bs): - return bs.decode("latin-1") - def byte_string(s): - return isinstance(s, bytes) - - def concat_buffers(a, b): - return a + b - - from io import BytesIO - from io import StringIO - from sys import maxsize as maxint - - iter_range = range - - def is_native_int(x): - return isinstance(x, int) - - def is_string(x): - return isinstance(x, str) - - def is_bytes(x): - return isinstance(x, bytes) or \ - isinstance(x, bytearray) or \ - isinstance(x, memoryview) - - from abc import ABC - - FileNotFoundError = FileNotFoundError - - -def _copy_bytes(start, end, seq): - """Return an immutable copy of a sequence (byte string, byte array, memoryview) - in a certain interval [start:seq]""" - - if isinstance(seq, memoryview): - return seq[start:end].tobytes() - elif isinstance(seq, bytearray): - return bytes(seq[start:end]) - else: - return seq[start:end] - -del sys -del abc diff --git a/resources/lib/deps/Cryptodome/Util/py3compat.pyi b/resources/lib/deps/Cryptodome/Util/py3compat.pyi deleted file mode 100644 index 74e04a2..0000000 --- a/resources/lib/deps/Cryptodome/Util/py3compat.pyi +++ /dev/null @@ -1,33 +0,0 @@ -from typing import Union, Any, Optional, IO - -Buffer = Union[bytes, bytearray, memoryview] - -import sys - -def b(s: str) -> bytes: ... -def bchr(s: int) -> bytes: ... -def bord(s: bytes) -> int: ... -def tobytes(s: Union[bytes, str]) -> bytes: ... -def tostr(b: bytes) -> str: ... -def bytestring(x: Any) -> bool: ... - -def is_native_int(s: Any) -> bool: ... -def is_string(x: Any) -> bool: ... -def is_bytes(x: Any) -> bool: ... - -def BytesIO(b: bytes) -> IO[bytes]: ... -def StringIO(s: str) -> IO[str]: ... - -if sys.version_info[0] == 2: - from sys import maxint - iter_range = xrange - -else: - from sys import maxsize as maxint - iter_range = range - -class FileNotFoundError: - def __init__(self, err: int, msg: str, filename: str) -> None: - pass - -def _copy_bytes(start: Optional[int], end: Optional[int], seq: Buffer) -> bytes: ... diff --git a/resources/lib/deps/Cryptodome/Util/strxor.py b/resources/lib/deps/Cryptodome/Util/strxor.py deleted file mode 100644 index 6b16155..0000000 --- a/resources/lib/deps/Cryptodome/Util/strxor.py +++ /dev/null @@ -1,146 +0,0 @@ -# =================================================================== -# -# Copyright (c) 2014, Legrandin -# 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. -# -# 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 HOLDER 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. -# =================================================================== - -from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, c_size_t, - create_string_buffer, get_raw_buffer, - c_uint8_ptr, is_writeable_buffer) - -_raw_strxor = load_pycryptodome_raw_lib( - "Cryptodome.Util._strxor", - """ - void strxor(const uint8_t *in1, - const uint8_t *in2, - uint8_t *out, size_t len); - void strxor_c(const uint8_t *in, - uint8_t c, - uint8_t *out, - size_t len); - """) - - -def strxor(term1, term2, output=None): - """From two byte strings of equal length, - create a third one which is the byte-by-byte XOR of the two. - - Args: - term1 (bytes/bytearray/memoryview): - The first byte string to XOR. - term2 (bytes/bytearray/memoryview): - The second byte string to XOR. - output (bytearray/memoryview): - The location where the result will be written to. - It must have the same length as ``term1`` and ``term2``. - If ``None``, the result is returned. - :Return: - If ``output`` is ``None``, a new byte string with the result. - Otherwise ``None``. - - .. note:: - ``term1`` and ``term2`` must have the same length. - """ - - if len(term1) != len(term2): - raise ValueError("Only byte strings of equal length can be xored") - - if output is None: - result = create_string_buffer(len(term1)) - else: - # Note: output may overlap with either input - result = output - - if not is_writeable_buffer(output): - raise TypeError("output must be a bytearray or a writeable memoryview") - - if len(term1) != len(output): - raise ValueError("output must have the same length as the input" - " (%d bytes)" % len(term1)) - - _raw_strxor.strxor(c_uint8_ptr(term1), - c_uint8_ptr(term2), - c_uint8_ptr(result), - c_size_t(len(term1))) - - if output is None: - return get_raw_buffer(result) - else: - return None - - -def strxor_c(term, c, output=None): - """From a byte string, create a second one of equal length - where each byte is XOR-red with the same value. - - Args: - term(bytes/bytearray/memoryview): - The byte string to XOR. - c (int): - Every byte in the string will be XOR-ed with this value. - It must be between 0 and 255 (included). - output (None or bytearray/memoryview): - The location where the result will be written to. - It must have the same length as ``term``. - If ``None``, the result is returned. - - Return: - If ``output`` is ``None``, a new ``bytes`` string with the result. - Otherwise ``None``. - """ - - if not 0 <= c < 256: - raise ValueError("c must be in range(256)") - - if output is None: - result = create_string_buffer(len(term)) - else: - # Note: output may overlap with either input - result = output - - if not is_writeable_buffer(output): - raise TypeError("output must be a bytearray or a writeable memoryview") - - if len(term) != len(output): - raise ValueError("output must have the same length as the input" - " (%d bytes)" % len(term)) - - _raw_strxor.strxor_c(c_uint8_ptr(term), - c, - c_uint8_ptr(result), - c_size_t(len(term)) - ) - - if output is None: - return get_raw_buffer(result) - else: - return None - - -def _strxor_direct(term1, term2, result): - """Very fast XOR - check conditions!""" - _raw_strxor.strxor(term1, term2, result, c_size_t(len(term1))) diff --git a/resources/lib/deps/Cryptodome/Util/strxor.pyi b/resources/lib/deps/Cryptodome/Util/strxor.pyi deleted file mode 100644 index ca896f3..0000000 --- a/resources/lib/deps/Cryptodome/Util/strxor.pyi +++ /dev/null @@ -1,6 +0,0 @@ -from typing import Union, Optional - -Buffer = Union[bytes, bytearray, memoryview] - -def strxor(term1: bytes, term2: bytes, output: Optional[Buffer]=...) -> bytes: ... -def strxor_c(term: bytes, c: int, output: Optional[Buffer]=...) -> bytes: ... diff --git a/resources/lib/deps/Cryptodome/__init__.py b/resources/lib/deps/Cryptodome/__init__.py deleted file mode 100644 index c33481e..0000000 --- a/resources/lib/deps/Cryptodome/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -__all__ = ['Cipher', 'Hash', 'Protocol', 'PublicKey', 'Util', 'Signature', - 'IO', 'Math'] - -version_info = (3, 20, '0') - -__version__ = ".".join([str(x) for x in version_info]) diff --git a/resources/lib/deps/Cryptodome/__init__.pyi b/resources/lib/deps/Cryptodome/__init__.pyi deleted file mode 100644 index bc73446..0000000 --- a/resources/lib/deps/Cryptodome/__init__.pyi +++ /dev/null @@ -1,4 +0,0 @@ -from typing import Tuple, Union - -version_info : Tuple[int, int, Union[int, str]] -__version__ : str diff --git a/resources/lib/deps/Cryptodome/py.typed b/resources/lib/deps/Cryptodome/py.typed deleted file mode 100644 index e69de29..0000000