diff --git a/Pipfile b/Pipfile index 91c273453..32c3facc0 100644 --- a/Pipfile +++ b/Pipfile @@ -1,8 +1,10 @@ [[source]] + url = "https://pypi.python.org/simple" verify_ssl = true name = "pypi" + [packages] rpcudp = {git = "https://github.com/nucypher/rpcudp", ref = "kms-dependency"} @@ -12,17 +14,21 @@ lmdb = "*" pynacl = "*" "pysha3" = "*" bidict = "*" -"py-ecc" = "*" +py-ecc = "*" sqlalchemy = "*" twisted = "*" pyopenssl = "*" -"service-identity" = "*" +service-identity = "*" +apistar = "*" +mypy = "*" +pytest-mypy = "*" + [dev-packages] pytest = "*" coverage = "*" -"pytest-cov" = "*" +pytest-cov = "*" pdbpp = "*" ipython = "*" appdirs = "*" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 000000000..93da13e96 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,695 @@ +{ + "_meta": { + "hash": { + "sha256": "f34600b0576547b08dbe3f8d0563a6fc7019bad8faff15ef2f0ade2fedab7af1" + }, + "host-environment-markers": { + "implementation_name": "cpython", + "implementation_version": "3.6.3", + "os_name": "posix", + "platform_machine": "x86_64", + "platform_python_implementation": "CPython", + "platform_release": "4.13.0-16-generic", + "platform_system": "Linux", + "platform_version": "#19-Ubuntu SMP Wed Oct 11 18:35:14 UTC 2017", + "python_full_version": "3.6.3", + "python_version": "3.6", + "sys_platform": "linux" + }, + "pipfile-spec": 6, + "requires": {}, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.python.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "apistar": { + "hashes": [ + "sha256:572cc48e27531607f2a8761009783de26daf60eb616f9b665e2c4cb8b23b473a" + ], + "version": "==0.3.9" + }, + "asn1crypto": { + "hashes": [ + "sha256:654b7db3b120e23474e9a1e5e38d268c77e58a9e17d2cb595456c37309846494", + "sha256:0874981329cfebb366d6584c3d16e913f2a0eb026c9463efcc4aaf42a9d94d70" + ], + "version": "==0.23.0" + }, + "attrs": { + "hashes": [ + "sha256:e7d51b70f19a4da5fe6b3c9938983e0af3b91e230edc504bd73c443d98037063", + "sha256:c78f53e32d7cf36d8597c8a2c7e3c0ad210f97b9509e152e4c37fa80869f823c" + ], + "version": "==17.3.0" + }, + "automat": { + "hashes": [ + "sha256:2140297df155f7990f6f4c73b2ab0583bd8150db9ed2a1b48122abe66e9908c1", + "sha256:3c1fd04ecf08ac87b4dd3feae409542e9bf7827257097b2b6ed5692f69d6f6a8" + ], + "version": "==0.6.0" + }, + "bidict": { + "hashes": [ + "sha256:d39699e569b757893cf0397148b3452357a95ecd88fd1ab0db663c77c93778f3" + ], + "version": "==0.13.1" + }, + "certifi": { + "hashes": [ + "sha256:244be0d93b71e93fc0a0a479862051414d0e00e16435707e5bf5000f92e04694", + "sha256:5ec74291ca1136b40f0379e1128ff80e866597e4e2c1e755739a913bbc3613c0" + ], + "version": "==2017.11.5" + }, + "cffi": { + "hashes": [ + "sha256:2c707e97ad7b0417713543be7cb87315c015bb5dd97903480168d60ebe3e313e", + "sha256:6d8c7e20eb90be9e1ccce8e8dd4ee5163b37289fc5708f9eeafc00adc07ba891", + "sha256:627298d788edcb317b6a01347428501e773f5e8f2988407231c07e50e3f6c1cf", + "sha256:bdd28cf8302eeca1b4c70ec727de384d4f6ea640b0e698934fd9b4c3bc88eeb1", + "sha256:248198cb714fe09f5c60b6acba3675d52199c6142641536796cdf89dd45e5590", + "sha256:c962cb68987cbfb70b034f153bfa467c615c0b55305d39b3237c4bdbdbc8b0f4", + "sha256:401ba2f6c1f1672b6c38670e1c00fa5f84f841edd30c32742dab5c7151cd89bf", + "sha256:1c103c0ee8235c47c4892288b2287014f33e7cb24b9d4a665be3aa744377dcb9", + "sha256:d7461ef8671ae40f991384bbc4a6b1b79f4e7175d8052584be44041996f46517", + "sha256:3ac9be5763238da1d6fa467c43e3f86472626837a478588c94165df09e62e120", + "sha256:d54a7c37f954fdbb971873c935a77ddc33690cec9b7ac254d9f948c43c32fa83", + "sha256:4d9bf1b23896bcd4d042e823f50ad36fb6d8e1e645a3dfb2fe2f070851489b92", + "sha256:61cf049b1c649d8eec360a1a1d09a61c37b9b2d542364506e8feb4afd232363d", + "sha256:ce3da410ae2ab8709565cc3b18fbe9a0eb96ea7b2189416098c48d839ecced84", + "sha256:e72d8b5056f967ecb57e166537408bc913f2f97dc568027fb6342fcfa9f81d64", + "sha256:11a8ba88ef6ae89110ef029dae7f1a293365e50bdd0c6ca973beed80cec95ae4", + "sha256:974f69112721ba2e8a6acd0f6b68a5e11432710a3eca4e4e6f4d7aaf99214ed1", + "sha256:062c66dabc3faf8e0db1ca09a6b8e308846e5d35f43bed1a68c492b0d96ac171", + "sha256:03a9b9efc280dbe6be149a7fa689f59a822df009eee633fdaf55a6f38795861f", + "sha256:8b3d6dc9981cedfb1ddcd4600ec0c7f5ac2c6ad2dc482011c7eecb4ae9c819e0", + "sha256:09b7d195d163b515ef7c2b2e26a689c9816c83d5319cceac6c36ffdab97ab048", + "sha256:943b94667749d1cfcd964e215a20b9c891deae913202ee8eacaf2b94164b155f", + "sha256:89829f5cfbcb5ad568a3d61bd23a8e33ad69b488d8f6a385e0097a4c20742a9b", + "sha256:ba78da7c940b041cdbb5aaff5afe11e8a8f25fe19564c12eefea5c5bd86930ca", + "sha256:a79b15b9bb4726672865cf5b0f63dee4835974a2b11b49652d70d49003f5d1f4", + "sha256:f6799913eb510b682de971ddef062bbb4a200f190e55cae81c413bc1fd4733c1", + "sha256:e7f5ad6b12f21b77d3a37d5c67260e464f4e9068eb0c0622f61d0e30390b31b6", + "sha256:5f96c92d5f5713ccb71e76dfa14cf819c59ecb9778e94bcb541e13e6d96d1ce5", + "sha256:5357b465e3d6b98972b7810f9969c913d365e75b09b7ba813f5f0577fe1ac9f4", + "sha256:75e1de9ba7c155d89bcf67d149b1c741df553c8158536e8d27e63167403159af", + "sha256:ab87dd91c0c4073758d07334c1e5f712ce8fe48f007b86f8238773963ee700a6" + ], + "version": "==1.11.2" + }, + "chardet": { + "hashes": [ + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691", + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae" + ], + "version": "==3.0.4" + }, + "constantly": { + "hashes": [ + "sha256:dd2fa9d6b1a51a83f0d7dd76293d734046aa176e384bf6e33b7e44880eb37c5d", + "sha256:586372eb92059873e29eba4f9dec8381541b4d3834660707faf8ba59146dfc35" + ], + "version": "==15.1.0" + }, + "coreapi": { + "hashes": [ + "sha256:bf39d118d6d3e171f10df9ede5666f63ad80bba9a29a8ec17726a66cf52ee6f3", + "sha256:46145fcc1f7017c076a2ef684969b641d18a2991051fddec9458ad3f78ffc1cb" + ], + "version": "==2.3.3" + }, + "coreschema": { + "hashes": [ + "sha256:5e6ef7bf38c1525d5e55a895934ab4273548629f16aed5c0a6caa74ebf45551f", + "sha256:9503506007d482ab0867ba14724b93c18a33b22b6d19fb419ef2d239dd4a1607" + ], + "version": "==0.0.4" + }, + "cryptography": { + "hashes": [ + "sha256:e130218bfb20d644510f24950cbeee350b15f4b318099b627c29975f12b9b7b3", + "sha256:f2ff1dda46f63b59bf01287c9a5bc8c8278b875a30c0ef26bac807ea4c1632ce", + "sha256:2d51a144f3447d0d87e07a433a11761e6b50c3ed89de6d0406e191d52612150f", + "sha256:4cc18262270dc0266934cfdfea7199dc65b9e3b31c68ead8685eb3649498dbe4", + "sha256:1fc1c6ad9f04871399de407a4f0f555adba5c7ec68068fd27d7ceee9e493755c", + "sha256:317ab5134ea176c03d068de5094e5b6ab580af2ba42ce596536bcc2e694057bc", + "sha256:af5b36499d6790480de0b9876982d027a698149c3f195c888be53fe48faff8e7", + "sha256:58d4c74cd6e6f54a60fd32874c03ba6230c9a1673699ee16811a6b96f91faf56", + "sha256:05cb4130ebe2d591141501ed06b85072cb3be5e5a0e943a5c487bd6858adcf64", + "sha256:791e228b5df8f124bfa33384195864cb9f5420b619580258d9002f14e625312e", + "sha256:b03dc0e2ab4bf02b43cf37ecc994344dc34e90567a8a563fb7538832475974c1", + "sha256:2ec3de13c3b0c5901820a58c337aca0f00be185c49bfc2c07eee0fe0af201c64", + "sha256:af8a9241bc8e1d2c9f10b7f5c3be8540af0c20b8e9af8c8cf4412971b7f78de5", + "sha256:57b7f8be4c817032dcd2c94f4dac6204ec2e85ef1881b4a660e56e7a63529eeb", + "sha256:35eb35340fdc0b772301f9de985db8d732f3c79dbd647d06b9a8e4e111b53950", + "sha256:12a16d4c7324166d78e112892236dd07e9b734cbee267ebf58a66c0f2a6fb3ae", + "sha256:346db72935450d2fb5c807e7f2051830e9bd33ea9471cd14bbf585ea2d5b7c0d", + "sha256:d4dbf045ee55aabdeb1e8e7550783f42c6f51d70a6069bd63669f34a4408b506", + "sha256:3beb79972cc26fa7fb553e59a0e96e476cd73c29c3d80456ac6562e7b217a677", + "sha256:2d72c8cd1e2be9942052b85b1481c74b2eb36780889696ce66afe602c04b9c67", + "sha256:0764c38c8e2e83238be5821757271cd3ef91dc3ee5bd7915c6b8e255bf1ad5c8", + "sha256:06c5a28e12539485c0c9e2e561335b835f5f0fdf2d5700b49835bad8607952ba", + "sha256:68a26c353627163d74ee769d4749f2ee243866e9dac43c93bb33ebd8fbed1199" + ], + "version": "==2.1.3" + }, + "hyperlink": { + "hashes": [ + "sha256:1ec8e11fb4f5b330f25864bf8cfd3133dff1a3637dfd14fa441297df15fc7cf9", + "sha256:bc4ffdbde9bdad204d507bd8f554f16bba82dd356f6130cb16f41422909c33bc" + ], + "version": "==17.3.1" + }, + "idna": { + "hashes": [ + "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4", + "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f" + ], + "version": "==2.6" + }, + "incremental": { + "hashes": [ + "sha256:717e12246dddf231a349175f48d74d93e2897244939173b01974ab6661406b9f", + "sha256:7b751696aaf36eebfab537e458929e194460051ccad279c72b755a167eebd4b3" + ], + "version": "==17.5.0" + }, + "itypes": { + "hashes": [ + "sha256:c6e77bb9fd68a4bfeb9d958fea421802282451a25bac4913ec94db82a899c073" + ], + "version": "==1.1.0" + }, + "jinja2": { + "hashes": [ + "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd", + "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4" + ], + "version": "==2.10" + }, + "kademlia": { + "git": "https://github.com/nucypher/kademlia", + "ref": "kms-dependency" + }, + "lmdb": { + "hashes": [ + "sha256:724234c4df6a8ef987957b6db92417c42afc3091d88765e9a8e52143b1309948", + "sha256:348ee17a618ea3afb1c981d262bfa0f2105d2833858747ef1b2c7307a09b5b18", + "sha256:8d7b1ac8dcd06c0a4f01569f8b4dfdcccf8baeaf1fed58fe6ec686de811168e7", + "sha256:6ec5220c3b84469f613c1332363f8a316c4742236f380b7c4e20f317690c8ff7", + "sha256:46f331b63a6daacc4a160a7d587f1a0f2caebaf37a72d1884e4d80fc6539e2cc", + "sha256:335a549e8f93e69edf154f1b760f5225f0f4799b15e7e85892e6f05c131062bb", + "sha256:70d75e3def0d28e08c1684140a63edeb2bbc8a871a4d510e84460a3257ca8234", + "sha256:cb95bb4ffcb1b586f6034a3c60172a6ab757d09dc925e04c5d8e2ce8076230a7", + "sha256:1edd081cf17fa75ca93adc20bca17d0d87bbbc214cd7cfd397faa4d2a9f5acf2", + "sha256:e7cefb0677fd2f8bc2df45846022fca9d0d95d2fdbd8490d4c5ea4b32d5cd014", + "sha256:41a303e7fbfd8e54f32e66a91ff24631eccd2bc904780e833116891712e47b67", + "sha256:df568e740ba04287ae47e50992730717418a142772d1e8596638f4841103736d", + "sha256:f891bb6fb0f745191af601e036f92afc509ee291749e23f4c8ac88ed8fb3c993", + "sha256:c56981dde168316df6307dfd4fcb53363a424f01e4c1b8dddca1609f4fcea2e9", + "sha256:4068da0133368ffd79afddbc466e2d8b573e14e4047340f91d8ec0f4f9e27bec", + "sha256:31675ce3415172ab6da3606d9397b3326bed685d49b2b4db09de4cd10d14e698", + "sha256:6bd267e925aeaff5a7d1a035ade4c3db2b18be3c69e00a55ee7f91ebb63cf0ee", + "sha256:1ea17dabcbfc1824ebedb7b40250f8157e80c0316d03e2bca3dce173386cdb0b", + "sha256:43779624301537e4d7ee9512a2562ea10b937bca562b16ecddde97db1985cffc", + "sha256:d2bbe03e76fc7cd99d8c94102aa4a10b900544096fdd610746c7118bc56e9c2a", + "sha256:aca81006366fb8fadc64c1ce90995ed08fe7bb075f55bed59631078fd2d76a04", + "sha256:78f46e6bb36750ff94d87a05c3f22778e909b4b035fa6d9cdd4dafd4c9ef49e2", + "sha256:744b1d0d4d965e72cec69405e23dafff1f162595697bace489699e9b3f5818ea", + "sha256:ebf73217e208a6ff5d9fe4ecdaed08f09767aaaecc50ac4a10cbdc050f4e056a", + "sha256:e8280656248d7c10aadc74e694bdb0947c3dbb096f9d6dc42e2441471b070e52", + "sha256:146b085b698bf480b35da4eac3a0bd8e331e002a9bafa69842867a879258b775" + ], + "version": "==0.93" + }, + "markupsafe": { + "hashes": [ + "sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665" + ], + "version": "==1.0" + }, + "npre": { + "editable": true, + "git": "https://github.com/nucypher/nucypher-pre-python", + "ref": "kms-dependency" + }, + "py": { + "hashes": [ + "sha256:8cca5c229d225f8c1e3085be4fcf306090b00850fefad892f9d96c7b6e2f310f", + "sha256:ca18943e28235417756316bfada6cd96b23ce60dd532642690dcfdaba988a76d" + ], + "version": "==1.5.2" + }, + "py-ecc": { + "hashes": [ + "sha256:f747ea69d40160c0a158c116bc3656eece9457cec446c8bfaffffd62f835de56", + "sha256:c7808a70c08bfc5c07b328f4df4406cfd3e365dd81f63bdd997c3c1eae34334a" + ], + "version": "==1.4.2" + }, + "pyasn1": { + "hashes": [ + "sha256:5eac8d0c1c1282842c9145ae134990a1baf7616eb4cee9129213fad76eba4f54", + "sha256:960a603e677897ea29b9a1327b789b7b8a9e187a89e05fed9c3152b3b7c22954", + "sha256:4bb562b9f1fc4526028b2aa4dc027dce08f3ade0780abc593a02ce6dbade6e6c", + "sha256:8fa8884056bd5b2c92ca1685e6344121b6b43718b44f0c6eb223958003c9d14a", + "sha256:16e896433f84575f0636cd9aa8b24659689268a62e00f17235e1fc23c6b00b25", + "sha256:bb6f5d5507621e0298794bc3e75b8f963e886cee388a378ab58e5c35ac024276", + "sha256:60bf78784b117979f5517c38308f6965fff6c803660fbb16368d94433953b62a", + "sha256:4a677c6c9e484977ed6c6a93714ff06ac374220408afeaeef4ef2af652af0f3d", + "sha256:b16fb6097d00bbafc114861b16ea41cfe63e32fed1bdc7cd5905a3e02a992fa3", + "sha256:8212bde51ec192e30654efe10e636082738ed728e316049f3685d66b8c92941c", + "sha256:8d4f0971682203bdfc93740ee7d3fcba0a7f55629451dbe2d32af2335c55b2be", + "sha256:187f2a66d617683f8e82d5c00033b7c8a0287e1da88a9d577aebec321cad4965" + ], + "version": "==0.3.7" + }, + "pyasn1-modules": { + "hashes": [ + "sha256:ea8b89f79724c3cf4ca88bc7327964f0750e5219618805dcc85ca0fdae9e5b34", + "sha256:16b086729c7af47a67c9e64cea2f763975b602155319b6c63f1f20e5f0179be7", + "sha256:7fc70766b8ef5a62eb43767cd7a1c1b3a6aa5095b263a6f2a734987fd90a35d6", + "sha256:92caf877c06c033786f0149dd37ea1abfd2c398a007bb40ae6b1f2c96804c1b2", + "sha256:018225e6718cfff7e515bd23efe8c0956e5226e3a416ba829e695c607e8ac58f", + "sha256:773641c73f6eaac19b5ed7c3e6c3e733dc43b494282ef067325ea6583763f531", + "sha256:3350c74c22eb821acfd22ecf4bbb9abfc1de2bd5befb5befd5b1b7ede2d92ace", + "sha256:6c457b5037e6a145a43bf3b5b1db622d20a08d4d1ad9a9bdc22dbef7229b250c", + "sha256:d37774d5de3887b1cdce7415209e92da49fcd13b99db1c44c179a93a5f87c8b2", + "sha256:6d8ad92e399b3140259b2c5249c49e67806a3eee332ed3734da807733925f04e", + "sha256:b437be576bdf440fc0e9307a4334303d117a577f2d809ecb9abd715539cb0109", + "sha256:1d303eed5aa54cafeca209d16b8c7ea2c6064735fb61f1bee2e0ed63a0816988" + ], + "version": "==0.1.5" + }, + "pycparser": { + "hashes": [ + "sha256:99a8ca03e29851d96616ad0404b4aad7d9ee16f25c9f9708a11faf2810f7b226" + ], + "version": "==2.18" + }, + "pynacl": { + "hashes": [ + "sha256:64d5132fdb54bcfdcc7a531c03980d630469258d8e70d7594ea0d27366fcf834", + "sha256:aa788cff8c027d7d621806d740fb4759028b0bba6dcdf4f316092698ecaad9ab", + "sha256:352584f80d52d53849037d60b90be787638f86d0234209959f68496300425013", + "sha256:abe8bf8908e735db77fd88b972ab1de240962ef91e6b044942f60cfe5a785107", + "sha256:b83e4232b43a52c8802234d575f992f82c1e9c466acd911983613a3823c4dc4e", + "sha256:c7c165b5d43198751651b33d562e57a7bd55d57062678ad14453ef715be415c4", + "sha256:74101b214305b6856d20593ee7e9f77a8f12e24a19caeda7f5fef3638cd8fe17", + "sha256:df4e44c34f3d4d199a06e1c11b7a7357e9adfdfaab34211991b33a58a155daba", + "sha256:976667758a7eb2e555cefbd131df54eea9967002beb73d06f1943127993db566", + "sha256:77c3b6d6fbf8b2137d41be9aed9eff30232287aeba00a6d353aa48fc9de4c55e", + "sha256:2fda81371b8dae1d18e2df9ce06961b66ef0b4226c6c98287e3e4661d9e64317", + "sha256:5bd2f0652fafd61669cbeb6312e9a87817e2e9613a471c588fd2e108e3119bb8", + "sha256:d0c0ca0a0548ed67301424f143c2b8b9d97956b152f8a56a7ebbdadb2071737d", + "sha256:0a85d5dde05a48377675f0c60d4b96127cc1b70097e24f4a4557ae3fa18a0038", + "sha256:8e194ea19c447c4caa94a84316412ad11cfb61f029d408fd4bdc1164ec694578", + "sha256:c05b6e17903509b6fb24f1c41c9760a33a0e0ed2a5aedc8eb9d988e5226377ce", + "sha256:afd47e36aaad8a3b6b92c98e45ca0e7de9627b51d577920af2e2620794503ea1", + "sha256:ac23923052341f35369be898c9673774937aad500fd5693365e3e5e509cbc664", + "sha256:2a8efeb6d442322836e70e6df0094ce8ea46e156e2b4ea38a86fd553c5665fff", + "sha256:189410422028e7b0543dee6aca3da026bbd66bbad078143c46c5a3faf2733acb", + "sha256:5be1af61b69a8dcc0264777c206305a7156c216ba98f6a7864254dfe60be35cf", + "sha256:a018ee272dc064a50cd799fc265d634c54e64a8bdba7a914877f9e1d32184e5e", + "sha256:45c5bcdf8ddefe2e9381f5d37fe778bbda6991fe7004e0b1ea3570df2fc07207" + ], + "version": "==1.2.0" + }, + "pyopenssl": { + "hashes": [ + "sha256:aade9985b93eaec51b0c0a2a60d14bb8dcff1ff8e36fe542e3c22812ec07315e", + "sha256:29630b9064a82e04d8242ea01d7c93d70ec320f5e3ed48e95fcabc6b1d0f6c76" + ], + "version": "==17.3.0" + }, + "pysha3": { + "hashes": [ + "sha256:6e6a84efb7856f5d760ee55cd2b446972cb7b835676065f6c4f694913ea8f8d9", + "sha256:f9046d59b3e72aa84f6dae83a040bd1184ebd7fef4e822d38186a8158c89e3cf", + "sha256:68c3a60a39f9179b263d29e221c1bd6e01353178b14323c39cc70593c30f21c5", + "sha256:59111c08b8f34495575d12e5f2ce3bafb98bea470bc81e70c8b6df99aef0dd2f", + "sha256:9fdd28884c5d0b4edfed269b12badfa07f1c89dbc5c9c66dd279833894a9896b", + "sha256:41be70b06c8775a9e4d4eeb52f2f6a3f356f17539a54eac61f43a29e42fd453d", + "sha256:571a246308a7b63f15f5aa9651f99cf30f2a6acba18eddf28f1510935968b603", + "sha256:93abd775dac570cb9951c4e423bcb2bc6303a9d1dc0dc2b7afa2dd401d195b24", + "sha256:11a2ba7a2e1d9669d0052fc8fb30f5661caed5512586ecbeeaf6bf9478ab5c48", + "sha256:5ec8da7c5c70a53b5fa99094af3ba8d343955b212bc346a0d25f6ff75853999f", + "sha256:9c778fa8b161dc9348dc5cc361e94d54aa5ff18413788f4641f6600d4893a608", + "sha256:fd7e66999060d079e9c0e8893e78d8017dad4f59721f6fe0be6307cd32127a07", + "sha256:827b308dc025efe9b6b7bae36c2e09ed0118a81f792d888548188e97b9bf9a3d", + "sha256:4416f16b0f1605c25f627966f76873e432971824778b369bd9ce1bb63d6566d9", + "sha256:c93a2676e6588abcfaecb73eb14485c81c63b94fca2000a811a7b4fb5937b8e8", + "sha256:684cb01d87ed6ff466c135f1c83e7e4042d0fc668fa20619f581e6add1d38d77", + "sha256:386998ee83e313b6911327174e088021f9f2061cbfa1651b97629b761e9ef5c4", + "sha256:c7c2adcc43836223680ebdf91f1d3373543dc32747c182c8ca2e02d1b69ce030", + "sha256:cd5c961b603bd2e6c2b5ef9976f3238a561c58569945d4165efb9b9383b050ef", + "sha256:0060a66be16665d90c432f55a0ba1f6480590cfb7d2ad389e688a399183474f0", + "sha256:fe988e73f2ce6d947220624f04d467faf05f1bbdbc64b0a201296bb3af92739e" + ], + "version": "==1.0.2" + }, + "pytest": { + "hashes": [ + "sha256:241d7e7798d79192a123ceaf64c602b4d233eacf6d6e42ae27caa97f498b7dc6", + "sha256:6d5bd4f7113b444c55a3bbb5c738a3dd80d43563d063fc42dcb0aaefbdd78b81" + ], + "version": "==3.2.5" + }, + "requests": { + "hashes": [ + "sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b", + "sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e" + ], + "version": "==2.18.4" + }, + "rpcudp": { + "git": "https://github.com/nucypher/rpcudp", + "ref": "kms-dependency" + }, + "service-identity": { + "hashes": [ + "sha256:0e76f3c042cc0f5c7e6da002cf646f59dc4023962d1d1166343ce53bdad39e17", + "sha256:4001fbb3da19e0df22c47a06d29681a398473af4aa9d745eca525b3b2c2302ab" + ], + "version": "==17.0.0" + }, + "six": { + "hashes": [ + "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb", + "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9" + ], + "version": "==1.11.0" + }, + "sqlalchemy": { + "hashes": [ + "sha256:8b79a5ed91cdcb5abe97b0045664c55c140aec09e5dd5c01303e23de5fe7a95a" + ], + "version": "==1.1.15" + }, + "twisted": { + "hashes": [ + "sha256:7bc3cdfd1ca5e5b84c7936db3c2cb2feb7d5b77410e713fd346da095a3b6a1d2", + "sha256:716805e624f9396fcc1f47e8aef68e629fd31599a74855b6e1636122c042458d", + "sha256:0da1a7e35d5fcae37bc9c7978970b5feb3bc82822155b8654ec63925c05af75c" + ], + "version": "==17.9.0" + }, + "uritemplate": { + "hashes": [ + "sha256:01c69f4fe8ed503b2951bef85d996a9d22434d2431584b5b107b2981ff416fbd", + "sha256:1b9c467a940ce9fb9f50df819e8ddd14696f89b9a8cc87ac77952ba416e0a8fd", + "sha256:c02643cebe23fc8adb5e6becffe201185bf06c40bda5c0b4028a93f1527d011d" + ], + "version": "==3.0.0" + }, + "urllib3": { + "hashes": [ + "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b", + "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f" + ], + "version": "==1.22" + }, + "werkzeug": { + "hashes": [ + "sha256:e8549c143af3ce6559699a01e26fa4174f4c591dbee0a499f3cd4c3781cdec3d", + "sha256:903a7b87b74635244548b30d30db4c8947fe64c5198f58899ddcd3a13c23bb26" + ], + "version": "==0.12.2" + }, + "whitenoise": { + "hashes": [ + "sha256:15f43b2e701821b95c9016cf469d29e2a546cb1c7dead584ba82c36f843995cf", + "sha256:9d81515f2b5b27051910996e1e860b1332e354d9e7bcf30c98f21dcb6713e0dd" + ], + "version": "==3.3.1" + }, + "zope.interface": { + "hashes": [ + "sha256:9902d5fc11309e17cdce6574243dc114b9c30de5c60ab53c90f6e3e962688565", + "sha256:4cb1c56b0356da9a33249ef77a688c47107f54191c12a0055d284b6bee7f447e", + "sha256:ff20038fbc0e7ea050a7e28fcb8ae6ed8378a8d08ac70b848ea39960dda86bbf", + "sha256:f6868378fffbb8651f1f8a767d17e42aed39926c8f6bb9c56f184022fe6c2090", + "sha256:a6375035a4b45d199a8b990e3a2f6b71906c318c56dfc14b2d58350b6ca59392", + "sha256:dec19181cf6af58ccb8ba3fa3ca9d4ec555b2f3cb31f589f6e86d15df0926c31", + "sha256:b8f3491c9df4f0ffed32b275033e74041f420e5dcdefa4b1500d753c64ef42cf", + "sha256:5d8813e438ab67a793b09e1223742b757dd95a4a64d466855a53cb113cc9c9c4", + "sha256:5a8cc535f4212b134e66a3e1c6b93b19d453dbad0e2f89d0df2c01deefc8cad9", + "sha256:bd626cd76b7e5cbecac9d3e0dd8f98e3eada15ead95713238a523f877327633d", + "sha256:16fe824b3d93ee0629aa1f04848a1b515d6b5dc9e98cc7a04feaa35fdb0de5f1", + "sha256:f47d4138405eb67e5f059b9ab74e0a1147adc3277f5fe37d5bae5209b67e89e7", + "sha256:8dfdc1588db31895f81bcba6c36dc981b4cf4a526c62eae3745bbfbe102477ef", + "sha256:88e3d54e88a601f45d03e2a062d5d16852d20e0863a92c19260ae72e2586378a", + "sha256:3d033abd27cd54157cf42a3bfd4d8c28d7fc5c6f775df3332307d2632a79925b", + "sha256:a21d69de2ee89fc59de93e7a43c0379ecedb5149739ff94e910c2bf0ca18e181", + "sha256:aef398a5b92e70b8152d2c4850bad0fe185adb50d948f32d0bba5694d82b67c7", + "sha256:11b068fc9916556f3820f38c2376c28d8e55e4a2c51c34915aaac38b75706d2e", + "sha256:78321a6c0c8cc6ac928e44ef04d50384bc864a7f5e3c25b84110da2ede83739f", + "sha256:4be05f79e952793f31a0c2d6a0672c81a3300315da587ce6a590357595217005", + "sha256:1d954d557b63124a65f2247ac6ed66fa36df18d1e8538d08c9b432e808a634de", + "sha256:a16a3e07511fb6806bb48c8c661d38cdb91cd4bc6c2b6b0b173e72362ec1ceb4", + "sha256:d6d26d5dfbfd60c65152938fcb82f949e8dada37c041f72916fef6621ba5c5ce" + ], + "version": "==4.4.3" + } + }, + "develop": { + "appdirs": { + "hashes": [ + "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e", + "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92" + ], + "version": "==1.4.3" + }, + "certifi": { + "hashes": [ + "sha256:244be0d93b71e93fc0a0a479862051414d0e00e16435707e5bf5000f92e04694", + "sha256:5ec74291ca1136b40f0379e1128ff80e866597e4e2c1e755739a913bbc3613c0" + ], + "version": "==2017.11.5" + }, + "chardet": { + "hashes": [ + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691", + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae" + ], + "version": "==3.0.4" + }, + "codecov": { + "hashes": [ + "sha256:ad82f054837b02081f86ed1eb6c04cddc029fbc734eaf92ff73da1db3a79188b", + "sha256:db1c182ca896244d8644d8410a33f6f6dd1cc24d80209907a65077445923f00c" + ], + "version": "==2.0.9" + }, + "coverage": { + "hashes": [ + "sha256:d1ee76f560c3c3e8faada866a07a32485445e16ed2206ac8378bd90dadffb9f0", + "sha256:007eeef7e23f9473622f7d94a3e029a45d55a92a1f083f0f3512f5ab9a669b05", + "sha256:17307429935f96c986a1b1674f78079528833410750321d22b5fb35d1883828e", + "sha256:845fddf89dca1e94abe168760a38271abfc2e31863fbb4ada7f9a99337d7c3dc", + "sha256:3f4d0b3403d3e110d2588c275540649b1841725f5a11a7162620224155d00ba2", + "sha256:4c4f368ffe1c2e7602359c2c50233269f3abe1c48ca6b288dcd0fb1d1c679733", + "sha256:f8c55dd0f56d3d618dfacf129e010cbe5d5f94b6951c1b2f13ab1a2f79c284da", + "sha256:cdd92dd9471e624cd1d8c1a2703d25f114b59b736b0f1f659a98414e535ffb3d", + "sha256:2ad357d12971e77360034c1596011a03f50c0f9e1ecd12e081342b8d1aee2236", + "sha256:e9a0e1caed2a52f15c96507ab78a48f346c05681a49c5b003172f8073da6aa6b", + "sha256:eea9135432428d3ca7ee9be86af27cb8e56243f73764a9b6c3e0bda1394916be", + "sha256:700d7579995044dc724847560b78ac786f0ca292867447afda7727a6fbaa082e", + "sha256:66f393e10dd866be267deb3feca39babba08ae13763e0fc7a1063cbe1f8e49f6", + "sha256:5ff16548492e8a12e65ff3d55857ccd818584ed587a6c2898a9ebbe09a880674", + "sha256:d00e29b78ff610d300b2c37049a41234d48ea4f2d2581759ebcf67caaf731c31", + "sha256:87d942863fe74b1c3be83a045996addf1639218c2cb89c5da18c06c0fe3917ea", + "sha256:358d635b1fc22a425444d52f26287ae5aea9e96e254ff3c59c407426f44574f4", + "sha256:81912cfe276e0069dca99e1e4e6be7b06b5fc8342641c6b472cb2fed7de7ae18", + "sha256:079248312838c4c8f3494934ab7382a42d42d5f365f0cf7516f938dbb3f53f3f", + "sha256:b0059630ca5c6b297690a6bf57bf2fdac1395c24b7935fd73ee64190276b743b", + "sha256:493082f104b5ca920e97a485913de254cbe351900deed72d4264571c73464cd0", + "sha256:e3ba9b14607c23623cf38f90b23f5bed4a3be87cbfa96e2e9f4eabb975d1e98b", + "sha256:82cbd3317320aa63c65555aa4894bf33a13fb3a77f079059eb5935eea415938d", + "sha256:9721f1b7275d3112dc7ccf63f0553c769f09b5c25a26ee45872c7f5c09edf6c1", + "sha256:bd4800e32b4c8d99c3a2c943f1ac430cbf80658d884123d19639bcde90dad44a", + "sha256:f29841e865590af72c4b90d7b5b8e93fd560f5dea436c1d5ee8053788f9285de", + "sha256:f3a5c6d054c531536a83521c00e5d4004f1e126e2e2556ce399bef4180fbe540", + "sha256:dd707a21332615108b736ef0b8513d3edaf12d2a7d5fc26cd04a169a8ae9b526", + "sha256:2e1a5c6adebb93c3b175103c2f855eda957283c10cf937d791d81bef8872d6ca", + "sha256:f87f522bde5540d8a4b11df80058281ac38c44b13ce29ced1e294963dd51a8f8", + "sha256:a7cfaebd8f24c2b537fa6a271229b051cdac9c1734bb6f939ccfc7c055689baa", + "sha256:309d91bd7a35063ec7a0e4d75645488bfab3f0b66373e7722f23da7f5b0f34cc", + "sha256:0388c12539372bb92d6dde68b4627f0300d948965bbb7fc104924d715fdc0965", + "sha256:ab3508df9a92c1d3362343d235420d08e2662969b83134f8a97dc1451cbe5e84", + "sha256:43a155eb76025c61fc20c3d03b89ca28efa6f5be572ab6110b2fb68eda96bfea", + "sha256:f98b461cb59f117887aa634a66022c0bd394278245ed51189f63a036516e32de", + "sha256:b6cebae1502ce5b87d7c6f532fa90ab345cfbda62b95aeea4e431e164d498a3d", + "sha256:a4497faa4f1c0fc365ba05eaecfb6b5d24e3c8c72e95938f9524e29dadb15e76", + "sha256:2b4d7f03a8a6632598cbc5df15bbca9f778c43db7cf1a838f4fa2c8599a8691a", + "sha256:1afccd7e27cac1b9617be8c769f6d8a6d363699c9b86820f40c74cfb3328921c" + ], + "version": "==4.4.2" + }, + "decorator": { + "hashes": [ + "sha256:95a26b17806e284452bfd97fa20aa1e8cb4ee23542bda4dbac5e4562aa1642cd", + "sha256:7cb64d38cb8002971710c8899fbdfb859a23a364b7c99dab19d1f719c2ba16b5" + ], + "version": "==4.1.2" + }, + "fancycompleter": { + "hashes": [ + "sha256:d2522f1f3512371f295379c4c0d1962de06762eb586c199620a2a5d423539b12" + ], + "version": "==0.8" + }, + "idna": { + "hashes": [ + "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4", + "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f" + ], + "version": "==2.6" + }, + "ipython": { + "hashes": [ + "sha256:fcc6d46f08c3c4de7b15ae1c426e15be1b7932bcda9d83ce1a4304e8c1129df3", + "sha256:51c158a6c8b899898d1c91c6b51a34110196815cc905f9be0fa5878e19355608" + ], + "version": "==6.2.1" + }, + "ipython-genutils": { + "hashes": [ + "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", + "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8" + ], + "version": "==0.2.0" + }, + "jedi": { + "hashes": [ + "sha256:3af518490ffcd00a3074c135b42511e081575e9abd115c216a34491411ceebb0", + "sha256:f6d5973573e76b1fd2ea75f6dcd6445d02d41ff3af5fc61b275b4e323d1dd396" + ], + "version": "==0.11.0" + }, + "parso": { + "hashes": [ + "sha256:b573acb69f66a970197b5fdbbdfad3b8a417a520e383133b2b4e708f104bfc9a", + "sha256:c5279916bb417aa2bf634648ff895cf35dce371d7319744884827bfad06f8d7b" + ], + "version": "==0.1.0" + }, + "pdbpp": { + "hashes": [ + "sha256:dde77326e4ea41439c243ed065826d53539530eeabd1b6615aae15cfbb9fda05" + ], + "version": "==0.9.2" + }, + "pexpect": { + "hashes": [ + "sha256:2b50dd8caa5007b10b0afcf075095814780b104b4a5cf9d8fbdc8bbc754e5ca4", + "sha256:00ab0872f80f5db740499e7a1283a7c3b97bea542d72df84d83dea17d0afd2d9" + ], + "markers": "sys_platform != 'win32'", + "version": "==4.3.0" + }, + "pickleshare": { + "hashes": [ + "sha256:c9a2541f25aeabc070f12f452e1f2a8eae2abd51e1cd19e8430402bdf4c1d8b5", + "sha256:84a9257227dfdd6fe1b4be1319096c20eb85ff1e82c7932f36efccfe1b09737b" + ], + "version": "==0.7.4" + }, + "prompt-toolkit": { + "hashes": [ + "sha256:3f473ae040ddaa52b52f97f6b4a493cfa9f5920c255a12dc56a7d34397a398a4", + "sha256:1df952620eccb399c53ebb359cc7d9a8d3a9538cb34c5a1344bdbeb29fbcc381", + "sha256:858588f1983ca497f1cf4ffde01d978a3ea02b01c8a26a8bbc5cd2e66d816917" + ], + "version": "==1.0.15" + }, + "ptyprocess": { + "hashes": [ + "sha256:e8c43b5eee76b2083a9badde89fd1bbce6c8942d1045146e100b7b5e014f4f1a", + "sha256:e64193f0047ad603b71f202332ab5527c5e52aa7c8b609704fc28c0dc20c4365" + ], + "version": "==0.5.2" + }, + "py": { + "hashes": [ + "sha256:8cca5c229d225f8c1e3085be4fcf306090b00850fefad892f9d96c7b6e2f310f", + "sha256:ca18943e28235417756316bfada6cd96b23ce60dd532642690dcfdaba988a76d" + ], + "version": "==1.5.2" + }, + "pygments": { + "hashes": [ + "sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d", + "sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc" + ], + "version": "==2.2.0" + }, + "pytest": { + "hashes": [ + "sha256:241d7e7798d79192a123ceaf64c602b4d233eacf6d6e42ae27caa97f498b7dc6", + "sha256:6d5bd4f7113b444c55a3bbb5c738a3dd80d43563d063fc42dcb0aaefbdd78b81" + ], + "version": "==3.2.5" + }, + "pytest-cov": { + "hashes": [ + "sha256:890fe5565400902b0c78b5357004aab1c814115894f4f21370e2433256a3eeec", + "sha256:03aa752cf11db41d281ea1d807d954c4eda35cfa1b21d6971966cc041bbf6e2d" + ], + "version": "==2.5.1" + }, + "requests": { + "hashes": [ + "sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b", + "sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e" + ], + "version": "==2.18.4" + }, + "simplegeneric": { + "hashes": [ + "sha256:dc972e06094b9af5b855b3df4a646395e43d1c9d0d39ed345b7393560d0b9173" + ], + "version": "==0.8.1" + }, + "six": { + "hashes": [ + "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb", + "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9" + ], + "version": "==1.11.0" + }, + "traitlets": { + "hashes": [ + "sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9", + "sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835" + ], + "version": "==4.3.2" + }, + "urllib3": { + "hashes": [ + "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b", + "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f" + ], + "version": "==1.22" + }, + "wcwidth": { + "hashes": [ + "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c", + "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e" + ], + "version": "==0.1.7" + }, + "wmctrl": { + "hashes": [ + "sha256:d806f65ac1554366b6e31d29d7be2e8893996c0acbb2824bbf2b1f49cf628a13" + ], + "version": "==0.3" + } + } +} diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 000000000..f7f2bd728 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,2 @@ +[mypy] +ignore_missing_imports=True \ No newline at end of file diff --git a/nkms/characters.py b/nkms/characters.py index 0b60beff5..89469903f 100644 --- a/nkms/characters.py +++ b/nkms/characters.py @@ -1,6 +1,12 @@ import asyncio +import binascii +from logging import getLogger import msgpack +from apistar import http +from apistar.core import Route +from apistar.frameworks.wsgi import WSGIApp as App +from sqlalchemy.exc import IntegrityError from kademlia.network import Server from kademlia.utils import digest @@ -8,9 +14,10 @@ from nkms.crypto import api as API from nkms.crypto.api import secure_random, keccak_digest from nkms.crypto.constants import NOT_SIGNED, NO_DECRYPTION_PERFORMED from nkms.crypto.powers import CryptoPower, SigningPower, EncryptingPower +from nkms.crypto.signature import Signature +from nkms.crypto.utils import BytestringSplitter from nkms.keystore.keypairs import Keypair from nkms.network import blockchain_client -from nkms.network.blockchain_client import list_all_ursulas from nkms.network.protocols import dht_value_splitter from nkms.network.server import NuCypherDHTServer, NuCypherSeedOnlyDHTServer from nkms.policy.constants import NOT_FROM_ALICE @@ -25,11 +32,8 @@ class Character(object): _default_crypto_powerups = None _seal = None - class NotFound(KeyError): - """raised when we try to interact with an actor of whom we haven't learned yet.""" - def __init__(self, attach_server=True, crypto_power: CryptoPower = None, - crypto_power_ups=[], is_me=True): + crypto_power_ups=[], is_me=True) -> None: """ :param attach_server: Whether to attach a Server when this Character is born. :param crypto_power: A CryptoPower object; if provided, this will be the character's CryptoPower. @@ -38,7 +42,14 @@ class Character(object): If neither crypto_power nor crypto_power_ups are provided, we give this Character all CryptoPowerUps listed in their _default_crypto_powerups attribute. + + :param is_me: Set this to True when you want this Character to represent the owner of the configuration under + which the program is being run. A Character who is_me can do things that other Characters can't, like run + servers, sign messages, and decrypt messages which are encrypted for them. Typically this will be True + for exactly one Character, but there are scenarios in which its imaginable to be represented by zero Characters + or by more than one Character. """ + self.log = getLogger("characters") if crypto_power and crypto_power_ups: raise ValueError("Pass crypto_power or crypto_power_ups (or neither), but not both.") @@ -59,6 +70,13 @@ class Character(object): else: self._seal = StrangerSeal(self) + class NotFound(KeyError): + """raised when we try to interact with an actor of whom we haven't learned yet.""" + + @classmethod + def from_pubkey_sig_bytes(cls, pubkey_sig_bytes): + return cls(is_me=False, crypto_power_ups=[SigningPower(keypair=Keypair.deserialize_key(pubkey_sig_bytes))]) + def attach_server(self, ksize=20, alpha=3, id=None, storage=None, *args, **kwargs) -> None: self._server = self._server_class(ksize, alpha, id, storage, *args, **kwargs) @@ -87,7 +105,7 @@ class Character(object): def learn_about_actor(self, actor): self._actor_mapping[actor.id()] = actor - def encrypt_for(self, recipient: str, cleartext: bytes, sign: bool = True, + def encrypt_for(self, recipient: "Character", cleartext: bytes, sign: bool = True, sign_cleartext=True) -> tuple: """ Looks up recipient actor, finds that actor's pubkey_enc on our keyring, and encrypts for them. @@ -101,20 +119,24 @@ class Character(object): """ actor = self._lookup_actor(recipient) - ciphertext = self._crypto_power.encrypt_for(actor.public_key(EncryptingPower), - cleartext) if sign: if sign_cleartext: signature = self.seal(cleartext) + ciphertext = self._crypto_power.encrypt_for(actor.public_key(EncryptingPower), + signature + cleartext) else: + ciphertext = self._crypto_power.encrypt_for(actor.public_key(EncryptingPower), + cleartext) signature = self.seal(ciphertext) else: signature = NOT_SIGNED + ciphertext = self._crypto_power.encrypt_for(actor.public_key(EncryptingPower), + cleartext) return ciphertext, signature - def verify_from(self, actor_whom_sender_claims_to_be: "Character", signature: bytes, - message: bytes, decrypt=False, + def verify_from(self, actor_whom_sender_claims_to_be: "Character", message: bytes, signature: Signature = None, + decrypt=False, signature_is_on_cleartext=False) -> tuple: """ Inverse of encrypt_for. @@ -125,11 +147,15 @@ class Character(object): :param signature_is_on_cleartext: True if we expect the signature to be on the cleartext. Otherwise, we presume that the ciphertext is what is signed. :return: (Whether or not the signature is valid, the decrypted plaintext or NO_DECRYPTION_PERFORMED) """ + if not signature and not signature_is_on_cleartext: + raise ValueError("You need to either provide the Signature or decrypt and find it on the cleartext.") + cleartext = NO_DECRYPTION_PERFORMED + if signature_is_on_cleartext: if decrypt: cleartext = self._crypto_power.decrypt(message) - message = cleartext + signature, message = BytestringSplitter(Signature)(cleartext, return_remainder=True) else: raise ValueError( "Can't look for a signature on the cleartext if we're not decrypting.") @@ -175,7 +201,7 @@ class Alice(Character): :return: Tuple(kfrags, eph_key_data) """ kfrags, eph_key_data = API.ecies_ephemeral_split_rekey( - alice_privkey, bytes(bob.seal), m, n) + alice_privkey, bytes(bob.seal.without_metabytes()), m, n) return (kfrags, eph_key_data) def publish_treasure_map(self, policy_group): @@ -228,7 +254,7 @@ class Bob(Character): self._ursulas[ursula_interface_id] = Ursula.as_discovered_on_network(port=port, interface=interface, pubkey_sig_bytes=ursula_pubkey_sig) - def get_treasure_map(self, policy_group, signature): + def get_treasure_map(self, policy_group): dht_key = policy_group.treasure_map_dht_key() @@ -237,8 +263,9 @@ class Bob(Character): packed_encrypted_treasure_map = event_loop.run_until_complete(ursula_coro) _signature_for_ursula, pubkey_sig_alice, hrac, encrypted_treasure_map = dht_value_splitter( packed_encrypted_treasure_map[5::], msgpack_remainder=True) - verified, packed_node_list = self.verify_from(self.alice, signature, encrypted_treasure_map, + verified, cleartext = self.verify_from(self.alice, encrypted_treasure_map, signature_is_on_cleartext=True, decrypt=True) + alices_signature, packed_node_list = BytestringSplitter(Signature)(cleartext, return_remainder=True) if not verified: return NOT_FROM_ALICE else: @@ -254,9 +281,23 @@ class Ursula(Character): interface = None interface_ttl = 0 + def __init__(self, urulsas_keystore=None, *args, **kwargs): + super().__init__(*args, **kwargs) + self.keystore = urulsas_keystore + + self._rest_app = None + + @property + def rest_app(self): + if not self._rest_app: + raise AttributeError( + "This Ursula doesn't have a REST app attached. If you want one, init with is_me and attach_server.") + else: + return self._rest_app + @staticmethod def as_discovered_on_network(port, interface, pubkey_sig_bytes): - ursula = Ursula(is_me=False, crypto_power_ups=[SigningPower(keypair=Keypair.deserialize_key(pubkey_sig_bytes))]) + ursula = Ursula.from_pubkey_sig_bytes(pubkey_sig_bytes) ursula.port = port ursula.interface = interface return ursula @@ -269,6 +310,12 @@ class Ursula(Character): super().attach_server(ksize, alpha, id, storage) + routes = [ + Route('/kFrag/{hrac_as_hex}', 'POST', self.set_policy), + ] + + self._rest_app = App(routes=routes) + def listen(self, port, interface): self.port = port self.interface = interface @@ -298,6 +345,24 @@ class Ursula(Character): loop = asyncio.get_event_loop() loop.run_until_complete(setter) + def set_policy(self, hrac_as_hex, request: http.Request): + """ + REST endpoint for setting a kFrag. + TODO: Instead of taking a Request, use the apistar typing system to type a payload and validate / split it. + TODO: Validate that the kfrag being saved is pursuant to an approved Policy (see #121). + """ + from nkms.policy.models import Policy # Avoid circular import + hrac = binascii.unhexlify(hrac_as_hex) + policy = Policy.from_ursula(request.body, self) + + try: + self.keystore.add_kfrag(hrac, policy.kfrag, policy.alices_signature) + except IntegrityError: + raise + # Do something appropriately RESTful (ie, 4xx). + + return # A 200, which whatever policy metadata. + class Seal(object): """ @@ -331,6 +396,9 @@ class Seal(object): def __len__(self): return len(bytes(self)) + def without_metabytes(self): + return self.character._crypto_power.pubkey_sig_bytes().without_metabytes() + class StrangerSeal(Seal): """ diff --git a/nkms/crypto/api.py b/nkms/crypto/api.py index 36cef91db..2bfa1ac86 100644 --- a/nkms/crypto/api.py +++ b/nkms/crypto/api.py @@ -4,8 +4,9 @@ from typing import Tuple, Union, List import sha3 from nacl.secret import SecretBox from py_ecc.secp256k1 import N, privtopub, ecdsa_raw_recover, ecdsa_raw_sign -from nkms.crypto import _internal +from nkms.crypto import _internal +from nkms.keystore.constants import SIG_KEYPAIR_BYTE, PUB_KEY_BYTE from npre import elliptic_curve from npre import umbral @@ -14,7 +15,7 @@ SYSTEM_RAND = SystemRandom() def secure_random( - num_bytes: int + num_bytes: int ) -> bytes: """ Returns an amount `num_bytes` of data from the OS's random device. @@ -27,13 +28,13 @@ def secure_random( :return: bytes """ # TODO: Should we just use os.urandom or avoid the import w/ this? - return SYSTEM_RAND.getrandbits(num_bytes*8).to_bytes(num_bytes, - byteorder='big') + return SYSTEM_RAND.getrandbits(num_bytes * 8).to_bytes(num_bytes, + byteorder='big') def secure_random_range( - min: int, - max: int + min: int, + max: int ) -> int: """ Returns a number from a secure random source betwee the range of @@ -48,7 +49,7 @@ def secure_random_range( def keccak_digest( - *messages: bytes + *messages: bytes ) -> bytes: """ Accepts an iterable containing bytes and digests it returning a @@ -66,7 +67,7 @@ def keccak_digest( def ecdsa_pub2bytes( - pubkey: Tuple[int] + pubkey: Tuple[int, int] ) -> bytes: """ Takes an ECDSA public key and converts to bytes. @@ -77,12 +78,12 @@ def ecdsa_pub2bytes( """ x = pubkey[0].to_bytes(32, byteorder='big') y = pubkey[1].to_bytes(32, byteorder='big') - return x+y + return x + y def ecdsa_bytes2pub( - pubkey: bytes -) -> Tuple[int]: + pubkey: bytes +) -> Tuple[int, int]: """ Takes a byte encoded ECDSA public key and converts to a Tuple of x, and y @@ -102,12 +103,12 @@ def ecdsa_gen_priv() -> bytes: :return: Byte encoded ECDSA privkey """ privkey = secure_random_range(1, N) - return privkey.to_bytes(32, byteorder='big') + return privkey.to_bytes(32, byteorder='big') # TODO: Add metabytes. def ecdsa_priv2pub( - privkey: bytes, - to_bytes: bool = True + privkey: bytes, + to_bytes: bool = True ) -> Union[bytes, Tuple[int]]: """ Returns the public component of an ECDSA private key. @@ -119,14 +120,14 @@ def ecdsa_priv2pub( """ pubkey = privtopub(privkey) if to_bytes: - return ecdsa_pub2bytes(pubkey) + return SIG_KEYPAIR_BYTE + PUB_KEY_BYTE + ecdsa_pub2bytes(pubkey) return pubkey def ecdsa_gen_sig( - v: int, - r: int, - s: int + v: int, + r: int, + s: int ) -> bytes: """ Generates an ECDSA signature, in bytes. @@ -137,15 +138,15 @@ def ecdsa_gen_sig( :return: bytestring of v, r, and s """ - v = v.to_bytes(1, byteorder='big') - r = r.to_bytes(32, byteorder='big') - s = s.to_bytes(32, byteorder='big') - return v+r+s + _v = v.to_bytes(1, byteorder='big') + _r = r.to_bytes(32, byteorder='big') + _s = s.to_bytes(32, byteorder='big') + return _v + _r + _s def ecdsa_load_sig( - signature: bytes -) -> Tuple[int]: + signature: bytes +) -> Tuple[int, int, int]: """ Loads an ECDSA signature, from a bytestring, to a tuple. @@ -160,9 +161,9 @@ def ecdsa_load_sig( def ecdsa_sign( - msghash: bytes, - privkey: bytes -) -> Tuple[int]: + msghash: bytes, + privkey: bytes +) -> Tuple[int, int, int]: """ Accepts a hashed message and signs it with the private key given. @@ -176,11 +177,11 @@ def ecdsa_sign( def ecdsa_verify( - v: int, - r: int, - s: int, - msghash: bytes, - pubkey: Union[bytes, Tuple[int]] + v: int, + r: int, + s: int, + msghash: bytes, + pubkey: Union[bytes, Tuple[int, int]] ) -> bool: """ Takes a v, r, s, a pubkey, and a hash of a message to verify via ECDSA. diff --git a/nkms/crypto/constants.py b/nkms/crypto/constants.py index e27c616cc..411a312ef 100644 --- a/nkms/crypto/constants.py +++ b/nkms/crypto/constants.py @@ -1,5 +1,4 @@ # TODO: Turn these into classes? -PUBKEY_SIG_LENGTH = 64 HASH_DIGEST_LENGTH = 32 NOT_SIGNED = 445 diff --git a/nkms/crypto/encrypting_keypair.py b/nkms/crypto/encrypting_keypair.py index bd6bba16b..945b5ac09 100644 --- a/nkms/crypto/encrypting_keypair.py +++ b/nkms/crypto/encrypting_keypair.py @@ -13,7 +13,7 @@ _tl.pre = None class EncryptingKeypair(object): KEYSIZE = 32 - def __init__(self, privkey: bytes = None): + def __init__(self, privkey: bytes = None) -> None: """ :privkey: Optional private key in a serialized form (32-byte string) If not given, a random one is generated. diff --git a/nkms/crypto/keypairs.py b/nkms/crypto/keypairs.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/nkms/crypto/powers.py b/nkms/crypto/powers.py index 2a20a5783..875f1914b 100644 --- a/nkms/crypto/powers.py +++ b/nkms/crypto/powers.py @@ -1,5 +1,5 @@ import inspect -from typing import Iterable, List, Tuple +from typing import Iterable, List, Tuple, Type from nkms.crypto import api as API from nkms.crypto.signature import Signature @@ -104,9 +104,9 @@ class CryptoPowerUp(object): class KeyPairBasedPower(CryptoPowerUp): - _keypair_class = None + _keypair_class = keypairs.Keypair - def __init__(self, keypair: keypairs.Keypair=None, pubkey_bytes: bytes=None): + def __init__(self, keypair: keypairs.Keypair=None, pubkey_bytes: bytes=None) -> None: if keypair and pubkey_bytes: raise ValueError("Pass keypair or pubkey_bytes (or neither), but not both.") elif keypair: diff --git a/nkms/crypto/signature.py b/nkms/crypto/signature.py index 9cb417ee6..9136dd593 100644 --- a/nkms/crypto/signature.py +++ b/nkms/crypto/signature.py @@ -1,4 +1,5 @@ from nkms.crypto import api as API +from nkms.keystore.keypairs import PublicKey class Signature(bytes): @@ -34,7 +35,7 @@ class Signature(bytes): def __repr__(self): return "{} v{}: {} - {}".format(__class__.__name__, self._v, self._r, self._s) - def verify(self, message: bytes, pubkey: bytes) -> bool: + def verify(self, message: bytes, pubkey: PublicKey) -> bool: """ Verifies that a message's signature was valid. @@ -43,8 +44,10 @@ class Signature(bytes): :return: True if valid, False if invalid """ + if not len(pubkey) == PublicKey._EXPECTED_LENGTH: + raise TypeError("Need a PublicKey of {} bytes to verify - got {}.".format(PublicKey._EXPECTED_LENGTH, len(pubkey))) msg_digest = API.keccak_digest(message) - return API.ecdsa_verify(self._v, self._r, self._s, msg_digest, pubkey) + return API.ecdsa_verify(self._v, self._r, self._s, msg_digest, pubkey.without_metabytes()) def __bytes__(self): """ diff --git a/nkms/keystore/constants.py b/nkms/keystore/constants.py index 9567b91f7..c71bad9ae 100644 --- a/nkms/keystore/constants.py +++ b/nkms/keystore/constants.py @@ -1,8 +1,8 @@ ENC_KEYPAIR_BYTE = b'E' SIG_KEYPAIR_BYTE = b'S' -REKEY_FRAG_ID_LEN = 32 -REKEY_FRAG_KEY_LEN = 32 +REKEY_FRAG_ID_LEN = 33 +REKEY_FRAG_KEY_LEN = 33 PUB_KEY_BYTE = b'0' PRIV_KEY_BYTE = b'1' diff --git a/nkms/keystore/keypairs.py b/nkms/keystore/keypairs.py index dfeb4f8e8..07da62376 100644 --- a/nkms/keystore/keypairs.py +++ b/nkms/keystore/keypairs.py @@ -15,7 +15,7 @@ class Keypair(object): # TODO: Throw error if a key is called and it doesn't exist # TODO: Maybe write a custome error ofr ^? - def __init__(self, privkey: bytes = None, pubkey: bytes = None): + def __init__(self, privkey: bytes = None, pubkey: "PublicKey" = None) -> None: if privkey and pubkey: self.privkey, self.pubkey = privkey, pubkey elif not privkey and not pubkey: @@ -27,7 +27,7 @@ class Keypair(object): self._gen_pubkey() elif pubkey and not privkey: # We have only the pubkey; this is a public-only pair. - self.pubkey = pubkey + self.pubkey = PublicKey(pubkey) self.public_only = True @staticmethod @@ -52,10 +52,25 @@ class Keypair(object): elif keypair_byte == constants.SIG_KEYPAIR_BYTE: if key_type_byte == constants.PUB_KEY_BYTE: - return SigningKeypair(pubkey=key) + return SigningKeypair(pubkey=key_data) # Kinda weird for the moment - we're using all 66 bytes here. TODO: Decide whether to make this the norm. elif key_type_byte == constants.PRIV_KEY_BYTE: return SigningKeypair(privkey=key) + else: + raise ValueError("Unable to determine which type of keypair this is - keypair_byte was {}".format(keypair_byte)) + assert False + + def gen_privkey(self): + """ + Generate the private key of the pair. + """ + return NotImplemented + + def _gen_pubkey(self): + """ + Generate the public key of the pair. + """ + return NotImplemented class EncryptingKeypair(Keypair): @@ -152,7 +167,7 @@ class SigningKeypair(Keypair): self._gen_pubkey() def _gen_pubkey(self): - self.pubkey = API.ecdsa_priv2pub(self.privkey) + self.pubkey = PublicKey(API.ecdsa_priv2pub(self.privkey)) def sign(self, msghash: bytes) -> bytes: """ @@ -175,7 +190,7 @@ class SigningKeypair(Keypair): :return: Boolean if the signature is valid """ v, r, s = API.ecdsa_load_sig(signature) - return API.ecdsa_verify(v, r, s, msghash, self.pubkey) + return API.ecdsa_verify(v, r, s, msghash, self.pubkey.without_metabytes()) def serialize_pubkey(self) -> bytes: """ @@ -198,3 +213,11 @@ class SigningKeypair(Keypair): constants.PRIV_KEY_BYTE + self.privkey) return serialized_key + + +class PublicKey(bytes): + _EXPECTED_LENGTH = 66 + _METABYTES_LENGTH = 2 + + def without_metabytes(self): + return self[self._METABYTES_LENGTH::] \ No newline at end of file diff --git a/nkms/keystore/keystore.py b/nkms/keystore/keystore.py index 2eb4755de..ef286b578 100644 --- a/nkms/keystore/keystore.py +++ b/nkms/keystore/keystore.py @@ -146,7 +146,7 @@ class KeyStore(object): kfrag_data = b'' if sig: kfrag_data += sig - kfrag_data += kfrag.id + kfrag.key + kfrag_data += bytes(kfrag) kfrag = KeyFrag(hrac, kfrag_data) self.session.add(kfrag) diff --git a/nkms/network/blockchain_client.py b/nkms/network/blockchain_client.py index a2d6fe93f..72055aa0e 100644 --- a/nkms/network/blockchain_client.py +++ b/nkms/network/blockchain_client.py @@ -1,4 +1 @@ -_ursulas_on_blockchain = [] - -def list_all_ursulas(): - return _ursulas_on_blockchain \ No newline at end of file +_ursulas_on_blockchain = [] \ No newline at end of file diff --git a/nkms/network/protocols.py b/nkms/network/protocols.py index 715721a65..eeece5e4c 100644 --- a/nkms/network/protocols.py +++ b/nkms/network/protocols.py @@ -1,17 +1,16 @@ -import msgpack - from kademlia.node import Node from kademlia.protocol import KademliaProtocol from kademlia.utils import digest from nkms.crypto.api import keccak_digest -from nkms.crypto.constants import PUBKEY_SIG_LENGTH, HASH_DIGEST_LENGTH +from nkms.crypto.constants import HASH_DIGEST_LENGTH from nkms.crypto.signature import Signature from nkms.crypto.utils import BytestringSplitter +from nkms.keystore.keypairs import PublicKey from nkms.network.constants import NODE_HAS_NO_STORAGE from nkms.network.node import NuCypherNode from nkms.network.routing import NuCypherRoutingTable -dht_value_splitter = BytestringSplitter(Signature, (bytes, PUBKEY_SIG_LENGTH), (bytes, HASH_DIGEST_LENGTH)) +dht_value_splitter = BytestringSplitter(Signature, PublicKey, (bytes, HASH_DIGEST_LENGTH)) class NuCypherHashProtocol(KademliaProtocol): @@ -46,13 +45,7 @@ class NuCypherHashProtocol(KademliaProtocol): def determine_legality_of_dht_key(self, signature, sender_pubkey_sig, message, hrac, dht_key, dht_value): proper_key = digest(keccak_digest(bytes(sender_pubkey_sig) + bytes(hrac))) - # TODO: This try block is not the right approach - a Ciphertext class can resolve this instead. - try: - # Ursula uaddr scenario - verified = signature.verify(hrac, sender_pubkey_sig) - except Exception as e: - # trmap scenario - verified = signature.verify(msgpack.dumps(message), sender_pubkey_sig) + verified = signature.verify(hrac, sender_pubkey_sig) if not verified or not proper_key == dht_key: self.log.warning("Got request to store illegal k/v: {} / {}".format(dht_key, dht_value)) diff --git a/nkms/policy/models.py b/nkms/policy/models.py index 6458f7a44..ea014b8e8 100644 --- a/nkms/policy/models.py +++ b/nkms/policy/models.py @@ -1,10 +1,18 @@ import msgpack -from npre.constants import UNKNOWN_KFRAG from nkms.characters import Alice, Bob, Ursula from nkms.crypto import api from nkms.crypto.api import keccak_digest +from nkms.crypto.constants import HASH_DIGEST_LENGTH, NOT_SIGNED from nkms.crypto.powers import EncryptingPower +from nkms.crypto.signature import Signature +from nkms.crypto.utils import BytestringSplitter +from nkms.keystore.keypairs import PublicKey +from npre.constants import UNKNOWN_KFRAG +from npre.umbral import RekeyFrag + +group_payload_splitter = BytestringSplitter(PublicKey) +policy_payload_splitter = BytestringSplitter((bytes, 66)) # TODO: I wish ReKeyFrag worked with this interface. class PolicyOffer(object): @@ -33,7 +41,7 @@ class PolicyManager(object): class PolicyManagerForAlice(PolicyManager): - def __init__(self, owner: Alice): + def __init__(self, owner: Alice) -> None: self.owner = owner def create_policy_group(self, @@ -54,6 +62,7 @@ class PolicyManagerForAlice(PolicyManager): for kfrag_id, rekey in enumerate(re_enc_keys): policy = Policy.from_alice( alice=self.owner, + bob=bob, kfrag=rekey, ) policies.append(policy) @@ -68,7 +77,7 @@ class PolicyGroup(object): _id = None - def __init__(self, uri: str, alice: Alice, bob: Bob, policies=None): + def __init__(self, uri: bytes, alice: Alice, bob: Bob, policies=None) -> None: self.policies = policies or [] self.alice = alice self.bob = bob @@ -82,11 +91,9 @@ class PolicyGroup(object): def hash(self, message): return keccak_digest(message) - def find_n_ursulas(self, networky_stuff, offer: PolicyOffer) -> list: + def find_n_ursulas(self, networky_stuff, offer: PolicyOffer): """ :param networky_stuff: A compliant interface (maybe a Client instance) to be used to engage the DHT swarm. - - :return: A list, with each element containing an Ursula and an OfferResult. """ for policy in self.policies: try: @@ -121,12 +128,15 @@ class PolicyGroup(object): """ return self.hash(bytes(self.alice.seal) + self.hrac()) - def transmit_payloads(self, networky_stuff): + def enact_policies(self, networky_stuff): for policy in self.policies: - payload = policy.encrypt_payload_for_ursula() - _response = networky_stuff.animate_policy(policy.ursula, - payload) # TODO: Parse response for confirmation and update TreasureMap with new Ursula friend. + policy_payload = policy.encrypt_payload_for_ursula() + full_payload = self.alice.seal + msgpack.dumps(policy_payload) + response = networky_stuff.enact_policy(policy.ursula, + self.hrac(), + full_payload) # TODO: Parse response for confirmation. + # Assuming response is what we hope for self.treasure_map.add_ursula(policy.ursula) @@ -152,9 +162,9 @@ class Policy(object): hashed_part = None _id = None - def __init__(self, alice, kfrag=UNKNOWN_KFRAG, challenge_size=20, set_id=True): + def __init__(self, alice, bob=None, kfrag=UNKNOWN_KFRAG, alices_signature=NOT_SIGNED, challenge_size=20, + set_id=True, encrypted_challenge_pack=None): """ - :param kfrag: The kFrag obviously, but defaults to UNKNOWN_KFRAG in case the user wants to set it later. :param deterministic_id_portion: Probably the fingerprint of Alice's public key. @@ -163,10 +173,15 @@ class Policy(object): :param challenge_size: The number of challenges to create in the ChallengePack. """ self.alice = alice + self.bob = bob + self.alices_signature = alices_signature self.kfrag = kfrag self.random_id_portion = api.secure_random(32) # TOOD: Where do we actually want this to live? self.challenge_size = challenge_size self.treasure_map = [] + self.challenge_pack = [] + + self._encrypted_challenge_pack = encrypted_challenge_pack @property def id(self): @@ -190,19 +205,55 @@ class Policy(object): @staticmethod def from_alice(kfrag, alice, + bob, ): - policy = Policy(alice, kfrag) + policy = Policy(alice, bob, kfrag) policy.generate_challenge_pack() return policy + @staticmethod + def from_ursula(group_payload, ursula): + alice_pubkey_sig, payload_encrypted_for_ursula = group_payload_splitter(group_payload, + msgpack_remainder=True) + alice = Alice.from_pubkey_sig_bytes(alice_pubkey_sig) + ursula.learn_about_actor(alice) + verified, cleartext = ursula.verify_from(alice, payload_encrypted_for_ursula, + decrypt=True, signature_is_on_cleartext=True) + + if not verified: + # TODO: What do we do if it's not signed properly? + pass + + alices_signature, policy_payload = BytestringSplitter(Signature)(cleartext, return_remainder=True) + + kfrag_bytes, encrypted_challenge_pack = policy_payload_splitter(policy_payload, return_remainder=True) + kfrag = RekeyFrag.from_bytes(kfrag_bytes) + policy = Policy(alice=alice, alices_signature=alices_signature, kfrag=kfrag, + encrypted_challenge_pack=encrypted_challenge_pack) + + return policy + def payload(self): - return msgpack.dumps({b"kf": bytes(self.kfrag), b"cp": msgpack.dumps(self.challenge_pack)}) + return bytes(self.kfrag) + msgpack.dumps(self.encrypted_treasure_map) def activate(self, ursula, negotiation_result): self.ursula = ursula self.negotiation_result = negotiation_result + @property + def encrypted_challenge_pack(self): + if not self._encrypted_challenge_pack: + if not self.bob: + raise TypeError("This Policy doesn't have a Bob, so there's no way to encrypt a ChallengePack for Bob.") + else: + self._encrypted_challenge_pack = self.alice.encrypt_for(self.bob, msgpack.dumps(self.challenge_pack)) + return self._encrypted_challenge_pack + + @encrypted_challenge_pack.setter + def encrypted_treasure_map(self, ecp): + self._encrypted_challenge_pack = ecp + def generate_challenge_pack(self): if self.kfrag == UNKNOWN_KFRAG: # TODO: Test this branch @@ -211,15 +262,15 @@ class Policy(object): # TODO: make this work instead of being random. See #46. import random - self.challenge_pack = [(random.getrandbits(32), random.getrandbits(32)) for x in - range(self.challenge_size)] + self._challenge_pack = [(random.getrandbits(32), random.getrandbits(32)) for x in + range(self.challenge_size)] return True def encrypt_payload_for_ursula(self): """ Craft an offer to send to Ursula. """ - return self.alice.encrypt_for(self.ursula, self.payload()) + return self.alice.encrypt_for(self.ursula, self.payload())[0] # We don't need the signature separately. class TreasureMap(object): diff --git a/tests/characters/test_crypto_characters_and_their_powers.py b/tests/characters/test_crypto_characters_and_their_powers.py index 81b5fba08..4b58ea100 100644 --- a/tests/characters/test_crypto_characters_and_their_powers.py +++ b/tests/characters/test_crypto_characters_and_their_powers.py @@ -47,7 +47,7 @@ def test_actor_with_signing_power_can_sign(): # ...or to get the signer's public key for verification purposes. sig = api.ecdsa_load_sig(bytes(signature)) - verification = api.ecdsa_verify(*sig, api.keccak_digest(message), seal_of_the_signer) + verification = api.ecdsa_verify(*sig, api.keccak_digest(message), seal_of_the_signer.without_metabytes()) assert verification is True @@ -71,7 +71,7 @@ def test_anybody_can_verify(): signature = alice.seal(message) # Our everyman can verify it. - verification, cleartext = somebody.verify_from(alice, signature, message, decrypt=False) + verification, cleartext = somebody.verify_from(alice, message, signature, decrypt=False) assert verification is True assert cleartext is NO_DECRYPTION_PERFORMED @@ -101,7 +101,7 @@ def test_signing_only_power_cannot_encrypt(): can_sign_but_not_encrypt.learn_about_actor(ursula) # The Character has the message ready... - cleartext = "This is Officer Rod Farva. Come in, Ursula! Come in Ursula!" + cleartext = b"This is Officer Rod Farva. Come in, Ursula! Come in Ursula!" # But without the proper PowerUp, no encryption happens. with pytest.raises(NoEncryptingPower) as e_info: @@ -122,4 +122,4 @@ def test_character_with_encrypting_power_can_encrypt(): ciphertext, signature = can_sign_and_encrypt.encrypt_for(ursula, cleartext, sign=False) assert signature == NOT_SIGNED - assert ciphertext is not None # annnd fail. + assert ciphertext is not None diff --git a/tests/conftest.py b/tests/conftest.py index df41f3322..ca2afe5be 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,7 +2,7 @@ import nkms.db import shutil import os import appdirs - +from .fixtures import * def pytest_runtest_setup(item): # Monkey-patching for tests so that we don't overwrite the default db diff --git a/tests/crypto/test_api.py b/tests/crypto/test_api.py index d5bbaf048..5b1c7fedb 100644 --- a/tests/crypto/test_api.py +++ b/tests/crypto/test_api.py @@ -1,10 +1,13 @@ -import unittest import random +import unittest + import sha3 from nacl.utils import EncryptedMessage -from npre import umbral -from npre import elliptic_curve as ec + from nkms.crypto import api +from nkms.keystore.keypairs import PublicKey +from npre import elliptic_curve as ec +from npre import umbral class TestCrypto(unittest.TestCase): @@ -85,7 +88,7 @@ class TestCrypto(unittest.TestCase): pubkey_bytes = api.ecdsa_priv2pub(privkey) self.assertEqual(bytes, type(pubkey_bytes)) - pubkey = api.ecdsa_bytes2pub(pubkey_bytes) + pubkey = api.ecdsa_bytes2pub(pubkey_bytes[PublicKey._METABYTES_LENGTH::]) self.assertEqual(tuple, type(pubkey)) self.assertEqual(2, len(pubkey)) self.assertEqual(int, type(pubkey[0])) @@ -106,7 +109,7 @@ class TestCrypto(unittest.TestCase): # Test Serialization pubkey = api.ecdsa_priv2pub(privkey) self.assertEqual(bytes, type(pubkey)) - self.assertEqual(64, len(pubkey)) + self.assertEqual(PublicKey._EXPECTED_LENGTH, len(pubkey)) # Test no serialization pubkey = api.ecdsa_priv2pub(privkey, to_bytes=False) @@ -271,7 +274,7 @@ class TestCrypto(unittest.TestCase): # Check no serialization rekey = api.ecies_rekey(self.privkey_a_bytes, self.privkey_b_bytes, - to_bytes=False) + to_bytes=False) self.assertEqual(umbral.RekeyFrag, type(rekey)) self.assertEqual(ec.ec_element, type(rekey.key)) @@ -283,7 +286,7 @@ class TestCrypto(unittest.TestCase): # Check with conversion frags = api.ecies_split_rekey(self.privkey_a_bytes, - self.privkey_b_bytes, 3, 4) + self.privkey_b_bytes, 3, 4) self.assertEqual(list, type(frags)) self.assertEqual(4, len(frags)) @@ -338,14 +341,10 @@ class TestCrypto(unittest.TestCase): self.assertEqual(32, len(plain_key)) rk_eb = api.ecies_rekey(eph_priv, self.privkey_b, - to_bytes=False) + to_bytes=False) self.assertEqual(umbral.RekeyFrag, type(rk_eb)) self.assertEqual(ec.ec_element, type(rk_eb.key)) reenc_key = api.ecies_reencrypt(rk_eb, enc_key) dec_key = api.ecies_decapsulate(self.privkey_b, reenc_key) self.assertEqual(plain_key, dec_key) - - def test_alpha_is_resolved(self): - with self.assertRaises(ImportError): - from nkms.crypto import _alpha diff --git a/tests/fixtures.py b/tests/fixtures.py new file mode 100644 index 000000000..7215ecdcf --- /dev/null +++ b/tests/fixtures.py @@ -0,0 +1,67 @@ +import datetime +import pytest + +from nkms.characters import congregate, Alice, Bob +from nkms.network import blockchain_client +from nkms.policy.constants import NON_PAYMENT +from nkms.policy.models import PolicyManagerForAlice, PolicyOffer +from tests.utilities import NUMBER_OF_URSULAS_IN_NETWORK, MockNetworkyStuff, make_ursulas, \ + URSULA_PORT, EVENT_LOOP + + +@pytest.fixture(scope="session") +def alices_policy_group(alice, bob): + """ + Creates a PolicyGroup, in a manner typical of how Alice might do it, with a unique uri. + """ + alice.__resource_id += b"/unique-again" # A unique name each time, like a path. + n = NUMBER_OF_URSULAS_IN_NETWORK + + policy_manager = PolicyManagerForAlice(alice) + + policy_group = policy_manager.create_policy_group( + bob, + alice.__resource_id, + m=3, + n=n, + ) + return policy_group + +@pytest.fixture(scope="session") +def enacted_policy_group(alices_policy_group, ursulas): + # Alice has a policy in mind and knows of enough qualifies Ursulas; she crafts an offer for them. + deposit = NON_PAYMENT + contract_end_datetime = datetime.datetime.now() + datetime.timedelta(days=5) + offer = PolicyOffer(alices_policy_group.n, deposit, contract_end_datetime) + + networky_stuff = MockNetworkyStuff(ursulas) + alices_policy_group.find_n_ursulas(networky_stuff, offer) + alices_policy_group.enact_policies(networky_stuff) + + return alices_policy_group + +@pytest.fixture(scope="session") +def alice(): + ALICE = Alice() + ALICE.attach_server() + ALICE.server.listen(8471) + ALICE.__resource_id = b"some_resource_id" + EVENT_LOOP.run_until_complete(ALICE.server.bootstrap([("127.0.0.1", URSULA_PORT)])) + return ALICE + + +@pytest.fixture(scope="session") +def bob(alice, ursulas): + BOB = Bob(alice=alice) + BOB.attach_server() + BOB.server.listen(8475) + EVENT_LOOP.run_until_complete(BOB.server.bootstrap([("127.0.0.1", URSULA_PORT)])) + congregate(alice, BOB, *ursulas) + return BOB + + +@pytest.fixture(scope="session") +def ursulas(): + URSULAS = make_ursulas(NUMBER_OF_URSULAS_IN_NETWORK, URSULA_PORT) + yield URSULAS + blockchain_client._ursulas_on_blockchain.clear() \ No newline at end of file diff --git a/tests/keystore/test_keypairs.py b/tests/keystore/test_keypairs.py index 2e11bc58b..616ab1c18 100644 --- a/tests/keystore/test_keypairs.py +++ b/tests/keystore/test_keypairs.py @@ -1,6 +1,7 @@ import unittest from nkms.crypto import api as API from nkms.keystore import keypairs +from nkms.keystore.keypairs import PublicKey class TestKeypairs(unittest.TestCase): @@ -27,8 +28,7 @@ class TestKeypairs(unittest.TestCase): self.assertEqual(32, len(self.ecdsa_keypair.privkey)) self.assertTrue(self.ecdsa_keypair.pubkey is not None) - self.assertEqual(bytes, type(self.ecdsa_keypair.pubkey)) - self.assertEqual(64, len(self.ecdsa_keypair.pubkey)) + self.assertEqual(PublicKey._EXPECTED_LENGTH, len(self.ecdsa_keypair.pubkey)) def test_ecdsa_keypair_signing(self): msghash = API.keccak_digest(b'hello world!') @@ -64,8 +64,7 @@ class TestKeypairs(unittest.TestCase): self.assertEqual(bytes, type(keypair.privkey)) self.assertEqual(32, len(keypair.privkey)) - self.assertEqual(bytes, type(keypair.pubkey)) - self.assertEqual(64, len(keypair.pubkey)) + self.assertEqual(PublicKey._EXPECTED_LENGTH, len(keypair.pubkey)) # Test no keys (key generation) keypair = keypairs.SigningKeypair() @@ -74,8 +73,7 @@ class TestKeypairs(unittest.TestCase): self.assertEqual(bytes, type(keypair.privkey)) self.assertEqual(32, len(keypair.privkey)) - self.assertEqual(bytes, type(keypair.pubkey)) - self.assertEqual(64, len(keypair.pubkey)) + self.assertEqual(PublicKey._EXPECTED_LENGTH, len(keypair.pubkey)) # Test privkey only keypair = keypairs.SigningKeypair(privkey=self.ecdsa_keypair.privkey) @@ -83,13 +81,10 @@ class TestKeypairs(unittest.TestCase): self.assertEqual(bytes, type(keypair.privkey)) self.assertEqual(32, len(keypair.privkey)) - - self.assertEqual(bytes, type(keypair.pubkey)) - self.assertEqual(64, len(keypair.pubkey)) + self.assertEqual(PublicKey._EXPECTED_LENGTH, len(keypair.pubkey)) # Test pubkey only keypair = keypairs.SigningKeypair(pubkey=self.ecdsa_keypair.pubkey) self.assertTrue(keypair.public_only is True) - self.assertEqual(bytes, type(keypair.pubkey)) - self.assertEqual(64, len(keypair.pubkey)) + self.assertEqual(PublicKey._EXPECTED_LENGTH, len(keypair.pubkey)) diff --git a/tests/keystore/test_keystore.py b/tests/keystore/test_keystore.py index 0392ca18b..652d799df 100644 --- a/tests/keystore/test_keystore.py +++ b/tests/keystore/test_keystore.py @@ -24,7 +24,6 @@ class TestKeyStore(unittest.TestCase): keypair = self.ks.generate_signing_keypair() self.assertEqual(keypairs.SigningKeypair, type(keypair)) self.assertEqual(bytes, type(keypair.privkey)) - self.assertEqual(bytes, type(keypair.pubkey)) def test_key_sqlite_keystore(self): keypair = self.ks.generate_encrypting_keypair() @@ -74,11 +73,11 @@ class TestKeyStore(unittest.TestCase): def test_keyfrag_sqlite(self): rand_sig = API.secure_random(65) - rand_id = API.secure_random(32) - rand_key = API.secure_random(32) + rand_id = b'\x00' + API.secure_random(32) + rand_key = b'\x00' + API.secure_random(32) rand_hrac = API.secure_random(32) - kfrag = RekeyFrag(id=rand_id, key=rand_key) + kfrag = RekeyFrag.from_bytes(rand_id+rand_key) self.ks.add_kfrag(rand_hrac, kfrag, sig=rand_sig) # Check that kfrag was added diff --git a/tests/network/test_network_actors.py b/tests/network/test_network_actors.py index 60573b22d..68306f38a 100644 --- a/tests/network/test_network_actors.py +++ b/tests/network/test_network_actors.py @@ -1,61 +1,38 @@ import asyncio -import datetime import msgpack import pytest from kademlia.utils import digest -from nkms.characters import Ursula, Alice, Character, Bob, congregate -from nkms.crypto.constants import PUBKEY_SIG_LENGTH, HASH_DIGEST_LENGTH +from nkms.characters import Ursula, Character from nkms.crypto.signature import Signature from nkms.crypto.utils import BytestringSplitter -from nkms.network.blockchain_client import list_all_ursulas +from nkms.network import blockchain_client from nkms.network.protocols import dht_value_splitter -from nkms.policy.constants import NON_PAYMENT -from nkms.policy.models import PolicyManagerForAlice, PolicyOffer, Policy -from tests.test_utilities import make_fake_ursulas, MockNetworkyStuff - -EVENT_LOOP = asyncio.get_event_loop() -asyncio.set_event_loop(EVENT_LOOP) - -URSULA_PORT = 7468 -NUMBER_OF_URSULAS_IN_NETWORK = 6 - -URSULAS, URSULA_PORTS = make_fake_ursulas(NUMBER_OF_URSULAS_IN_NETWORK, URSULA_PORT) - -ALICE = Alice() -ALICE.attach_server() -ALICE.server.listen(8471) -EVENT_LOOP.run_until_complete(ALICE.server.bootstrap([("127.0.0.1", URSULA_PORT)])) - -BOB = Bob(alice=ALICE) -BOB.attach_server() -BOB.server.listen(8475) -EVENT_LOOP.run_until_complete(BOB.server.bootstrap([("127.0.0.1", URSULA_PORT)])) - -congregate(ALICE, BOB, URSULAS[0]) +from nkms.policy.models import Policy +from tests.utilities import MockNetworkyStuff, EVENT_LOOP, URSULA_PORT, NUMBER_OF_URSULAS_IN_NETWORK -def test_all_ursulas_know_about_all_other_ursulas(): +def test_all_ursulas_know_about_all_other_ursulas(ursulas): """ Once launched, all Ursulas know about - and can help locate - all other Ursulas in the network. """ ignorance = [] - for acounter, announcing_ursula in enumerate(list_all_ursulas()): - for counter, propagating_ursula in enumerate(URSULAS): + for acounter, announcing_ursula in enumerate(blockchain_client._ursulas_on_blockchain): + for counter, propagating_ursula in enumerate(ursulas): if not digest(announcing_ursula) in propagating_ursula.server.storage: ignorance.append((counter, acounter)) if ignorance: pytest.fail(str(["{} didn't know about {}".format(counter, acounter) for counter, acounter in ignorance])) -def test_vladimir_illegal_interface_key_does_not_propagate(): +def test_vladimir_illegal_interface_key_does_not_propagate(ursulas): """ Although Ursulas propagate each other's interface information, as demonstrated above, they do not propagate interface information for Vladimir, an Evil Ursula. """ - vladimir = URSULAS[0] - ursula = URSULAS[1] + vladimir = ursulas[0] + ursula = ursulas[1] # Ursula hasn't seen any illegal keys. assert ursula.server.protocol.illegal_keys_seen == [] @@ -73,159 +50,153 @@ def test_vladimir_illegal_interface_key_does_not_propagate(): assert digest(illegal_key) in ursula.server.protocol.illegal_keys_seen -def test_alice_cannot_offer_policy_without_first_finding_ursula(): +def test_alice_cannot_offer_policy_without_first_finding_ursula(alice, bob, ursulas): """ Alice can't just offer a Policy if she doesn't know whether any Ursulas are available (she gets Ursula.NotFound). """ - networky_stuff = MockNetworkyStuff(URSULAS) - policy = Policy(Alice()) + networky_stuff = MockNetworkyStuff(ursulas) + policy = Policy(alice, bob) with pytest.raises(Ursula.NotFound): policy_offer = policy.encrypt_payload_for_ursula() -def test_trying_to_find_unknown_actor_raises_not_found(): +def test_trying_to_find_unknown_actor_raises_not_found(alice): """ Tony the test character can't make reference to a character he doesn't know about yet. """ tony_clifton = Character() message = b"some_message" - signature = ALICE.seal(message) + signature = alice.seal(message) # Tony can't reference Alice... with pytest.raises(Character.NotFound): - verification = tony_clifton.verify_from(ALICE, signature, message) + verification = tony_clifton.verify_from(alice, signature, message) # ...before learning about Alice. - tony_clifton.learn_about_actor(ALICE) - verification, NO_DECRYPTION_PERFORMED = tony_clifton.verify_from(ALICE, signature, message) + tony_clifton.learn_about_actor(alice) + verification, NO_DECRYPTION_PERFORMED = tony_clifton.verify_from(alice, message, signature) assert verification is True -def test_alice_finds_ursula(): +def test_alice_finds_ursula(alice, ursulas): """ With the help of any Ursula, Alice can find a specific Ursula. """ ursula_index = 1 - all_ursulas = list_all_ursulas() - getter = ALICE.server.get(all_ursulas[ursula_index]) + all_ursulas = blockchain_client._ursulas_on_blockchain + getter = alice.server.get(all_ursulas[ursula_index]) loop = asyncio.get_event_loop() value = loop.run_until_complete(getter) - _signature, _ursula_pubkey_sig, _hrac, interface_info = dht_value_splitter(value.lstrip(b"uaddr-"), return_remainder=True) + _signature, _ursula_pubkey_sig, _hrac, interface_info = dht_value_splitter(value.lstrip(b"uaddr-"), + return_remainder=True) port = msgpack.loads(interface_info)[0] assert port == URSULA_PORT + ursula_index -def test_alice_has_ursulas_public_key_and_uses_it_to_encode_policy_payload(): +def test_alice_creates_policy_group_with_correct_hrac(alices_policy_group): """ - Now that Alice has found an Ursula, Alice can make a PolicyGroup, using Ursula's Public Key to encrypt each offer. + Alice creates a PolicyGroup. It has the proper HRAC, unique per her, Bob, and the uri (resource_id). """ - # For example, a hashed path. - resource_id = b"as098duasdlkj213098asf" + alice = alices_policy_group.alice + bob = alices_policy_group.bob - # Alice has a policy in mind; she crafts an offer. - n = NUMBER_OF_URSULAS_IN_NETWORK - deposit = NON_PAYMENT - contract_end_datetime = datetime.datetime.now() + datetime.timedelta(days=5) - offer = PolicyOffer(n, deposit, contract_end_datetime) - - # Now, Alice needs to find N Ursulas to whom to make the offer. - networky_stuff = MockNetworkyStuff(URSULAS) - policy_manager = PolicyManagerForAlice(ALICE) - - policy_group = policy_manager.create_policy_group( - BOB, - resource_id, - m=3, - n=n, - ) - networky_stuff = MockNetworkyStuff(URSULAS) - policy_group.find_n_ursulas(networky_stuff, offer) - policy_group.transmit_payloads(networky_stuff) # Until we figure out encrypt_for logic - - return policy_group + assert alices_policy_group.hrac() == alices_policy_group.hash( + bytes(alice.seal) + bytes(bob.seal) + alice.__resource_id) -def test_alice_sets_treasure_map_on_network(): +def test_alice_enacts_policies_in_policy_group_via_rest(enacted_policy_group): """ - Having made a PolicyGroup, Alice creates a TreasureMap and sends it to Ursula via the DHT. + Now that Alice has made a PolicyGroup, she can enact its policies, using Ursula's Public Key to encrypt each offer + and transmitting them via REST. """ - policy_group = test_alice_has_ursulas_public_key_and_uses_it_to_encode_policy_payload() + ursula = enacted_policy_group.policies[0].ursula + kfrag_that_was_set = ursula.keystore.get_kfrag(enacted_policy_group.hrac()) + assert bool(kfrag_that_was_set) # TODO: This can be a more poignant assertion. - setter, encrypted_treasure_map, packed_encrypted_treasure_map, signature_for_bob, signature_for_ursula = ALICE.publish_treasure_map( - policy_group) + +def test_alice_sets_treasure_map_on_network(enacted_policy_group, ursulas): + """ + Having enacted all the policies of a PolicyGroup, Alice creates a TreasureMap and sends it to Ursula via the DHT. + """ + alice = enacted_policy_group.alice + setter, encrypted_treasure_map, packed_encrypted_treasure_map, signature_for_bob, signature_for_ursula = alice.publish_treasure_map( + enacted_policy_group) _set_event = EVENT_LOOP.run_until_complete(setter) - treasure_map_as_set_on_network = URSULAS[0].server.storage[ - digest(policy_group.treasure_map_dht_key())] + treasure_map_as_set_on_network = ursulas[0].server.storage[ + digest(enacted_policy_group.treasure_map_dht_key())] assert treasure_map_as_set_on_network == b"trmap" + packed_encrypted_treasure_map - return treasure_map_as_set_on_network, signature_for_bob, policy_group -def test_treasure_map_with_bad_id_does_not_propagate(): +def test_treasure_map_with_bad_id_does_not_propagate(alices_policy_group, ursulas): """ In order to prevent spam attacks, Ursula refuses to propagate a TreasureMap whose PolicyGroup ID does not comport to convention. """ illegal_policygroup_id = "This is not a conventional policygroup id" - policy_group = test_alice_has_ursulas_public_key_and_uses_it_to_encode_policy_payload() + alice = alices_policy_group.alice + bob = alices_policy_group.bob + treasure_map = alices_policy_group.treasure_map - treasure_map = policy_group.treasure_map + encrypted_treasure_map, signature = alice.encrypt_for(bob, treasure_map.packed_payload()) + packed_encrypted_treasure_map = msgpack.dumps(encrypted_treasure_map) # TODO: #114? Do we even need to pack here? - encrypted_treasure_map, signature = ALICE.encrypt_for(BOB, treasure_map.packed_payload()) - packed_encrypted_treasure_map = msgpack.dumps(encrypted_treasure_map) #TODO: #114? Do we even need to pack here? - - setter = ALICE.server.set(illegal_policygroup_id, packed_encrypted_treasure_map) + setter = alice.server.set(illegal_policygroup_id, packed_encrypted_treasure_map) _set_event = EVENT_LOOP.run_until_complete(setter) with pytest.raises(KeyError): - URSULAS[0].server.storage[digest(illegal_policygroup_id)] + ursulas[0].server.storage[digest(illegal_policygroup_id)] -def test_treasure_map_stored_by_ursula_is_the_correct_one_for_bob(): +def test_treasure_map_stored_by_ursula_is_the_correct_one_for_bob(alice, bob, ursulas, enacted_policy_group): """ The TreasureMap given by Alice to Ursula is the correct one for Bob; he can decrypt and read it. """ + treasure_map_as_set_on_network = ursulas[0].server.storage[ + digest(enacted_policy_group.treasure_map_dht_key())] - treasure_map_as_set_on_network, signature, policy_group = test_alice_sets_treasure_map_on_network() _signature_for_ursula, pubkey_sig_alice, hrac, encrypted_treasure_map = dht_value_splitter( treasure_map_as_set_on_network[5::], msgpack_remainder=True) # 5:: to account for prepended "trmap" - verified, treasure_map_as_decrypted_by_bob = BOB.verify_from(ALICE, signature, + verified, cleartext = treasure_map_as_decrypted_by_bob = bob.verify_from(alice, encrypted_treasure_map, decrypt=True, signature_is_on_cleartext=True, ) - assert treasure_map_as_decrypted_by_bob == policy_group.treasure_map.packed_payload() + _alices_signature, treasure_map_as_decrypted_by_bob = BytestringSplitter(Signature)(cleartext, return_remainder=True) + + assert treasure_map_as_decrypted_by_bob == enacted_policy_group.treasure_map.packed_payload() assert verified is True -def test_bob_can_retreive_the_treasure_map_and_decrypt_it(): +def test_bob_can_retreive_the_treasure_map_and_decrypt_it(enacted_policy_group, ursulas): """ Above, we showed that the TreasureMap saved on the network is the correct one for Bob. Here, we show that Bob can retrieve it with only the information about which he is privy pursuant to the PolicyGroup. """ - treasure_map_as_set_on_network, signature, policy_group = test_alice_sets_treasure_map_on_network() - networky_stuff = MockNetworkyStuff(URSULAS) + bob = enacted_policy_group.bob + networky_stuff = MockNetworkyStuff(ursulas) # Of course, in the real world, Bob has sufficient information to reconstitute a PolicyGroup, gleaned, we presume, # through a side-channel with Alice. - treasure_map_from_wire = BOB.get_treasure_map(policy_group, signature) + treasure_map_from_wire = bob.get_treasure_map(enacted_policy_group) - assert policy_group.treasure_map == treasure_map_from_wire + assert enacted_policy_group.treasure_map == treasure_map_from_wire -def test_treaure_map_is_legit(): +def test_treaure_map_is_legit(enacted_policy_group): """ Sure, the TreasureMap can get to Bob, but we also need to know that each Ursula in the TreasureMap is on the network. """ - treasure_map_as_set_on_network, signature, policy_group = test_alice_sets_treasure_map_on_network() - - for ursula_interface_id in policy_group.treasure_map: - getter = ALICE.server.get(ursula_interface_id) + alice = enacted_policy_group.alice + for ursula_interface_id in enacted_policy_group.treasure_map: + getter = alice.server.get(ursula_interface_id) loop = asyncio.get_event_loop() value = loop.run_until_complete(getter) - signature, ursula_pubkey_sig, hrac, interface_info = dht_value_splitter(value.lstrip(b"uaddr-"), return_remainder=True) + signature, ursula_pubkey_sig, hrac, interface_info = dht_value_splitter(value.lstrip(b"uaddr-"), + return_remainder=True) port = msgpack.loads(interface_info)[0] - assert port in URSULA_PORTS + legal_ports = range(NUMBER_OF_URSULAS_IN_NETWORK, NUMBER_OF_URSULAS_IN_NETWORK + URSULA_PORT) + assert port in legal_ports diff --git a/tests/network/test_network_traffic.py b/tests/network/test_network_traffic.py deleted file mode 100644 index c68cf4ea1..000000000 --- a/tests/network/test_network_traffic.py +++ /dev/null @@ -1,97 +0,0 @@ -import asyncio -# Kademlia emits a bunch of useful logging info; uncomment below to see it. -import logging - -from nkms.network.server import NuCypherSeedOnlyDHTServer, NuCypherDHTServer - -logging.basicConfig(level=logging.DEBUG) -import pytest - - -@pytest.mark.skip(reason="Strange. This appeared to be fixed in ab6cbaead69c9e0073de13c36078b09997d2a6ff, but still sometimes fails.") -def test_seed_only_node_does_not_store_anything(): - """ - Shows that when we set up two nodes, a "full" node and a "seed-only" node, - that the "seed-only" node can set key-value pairs that the "full" node will store, - but not vice-versa. - """ - - # First, let's set up two servers: - # A full node... - event_loop = asyncio.new_event_loop() - asyncio.set_event_loop(event_loop) - - full_server = NuCypherDHTServer() - full_server.listen(8468) - event_loop.run_until_complete(full_server.bootstrap([("127.0.0.1", 8468)])) - - # ...and a seed-only node. - seed_only_server = NuCypherSeedOnlyDHTServer() - seed_only_server.listen(8471) - event_loop.run_until_complete(seed_only_server.bootstrap([("127.0.0.1", 8468)])) - - # The seed-only node is able to set a key... - key_to_store = "llamas" - value_to_store = "tons_of_things_keyed_llamas" - setter = seed_only_server.set(key_to_store, value_to_store) - event_loop.run_until_complete(setter) - - # ...and retrieve it again. - getter = seed_only_server.get(key_to_store) - value = event_loop.run_until_complete(getter) - assert value == value_to_store - - # The item is stored on the full server. - full_server_stored_items = list(full_server.storage.items()) - assert len(full_server_stored_items) == 1 - assert full_server_stored_items[0][1] == value_to_store - - # ...but nothing is stored on the seed-only server. - seed_only_server_stored_items = list(seed_only_server.storage.items()) - assert len(seed_only_server_stored_items) == 0 - - # If the full server tries to store something... - key_that_is_not_stored = b"european_swallow" - value_that_is_not_stored = b"grip_it_by_the_husk" - setter = full_server.set(key_that_is_not_stored, value_that_is_not_stored) - event_loop.run_until_complete(setter) - - # ...it is *not* stored on the seed-only server. - assert len(list(seed_only_server.storage.items())) == 0 - - # annnnd stop. - seed_only_server.stop() - full_server.stop() - event_loop.close() - - -@pytest.mark.skip(reason="Strange. This appeared to be fixed in ab6cbaead69c9e0073de13c36078b09997d2a6ff, but still sometimes fails.") -def test_full_node_does_not_try_to_store_on_seed_only_node(): - """ - A full node is able to determine that a seed-only node does not have the capability - to store. It doesn't waste its time trying. - """ - event_loop = asyncio.new_event_loop() - asyncio.set_event_loop(event_loop) - - full_server = NuCypherDHTServer() - full_server.listen(8468) - event_loop.run_until_complete(full_server.bootstrap([("127.0.0.1", 8468)])) - - seed_only_server = NuCypherSeedOnlyDHTServer() - seed_only_server.listen(8471) - event_loop.run_until_complete(seed_only_server.bootstrap([("127.0.0.1", 8468)])) - - key_that_is_not_stored = b"european_swallow" - value_that_is_not_stored = b"grip_it_by_the_husk" - setter = full_server.set(key_that_is_not_stored, value_that_is_not_stored) - - # Here's the interesting part. - result = event_loop.run_until_complete(setter) - assert not result - assert full_server.digests_set == 0 - - # annnnd stop. - seed_only_server.stop() - full_server.stop() - event_loop.close() diff --git a/tests/network/test_network_upgrade.py b/tests/network/test_network_upgrade.py index ae8c81b04..e470412ed 100644 --- a/tests/network/test_network_upgrade.py +++ b/tests/network/test_network_upgrade.py @@ -1,11 +1,17 @@ -from tests.network.test_network_actors import test_alice_sets_treasure_map_on_network, BOB, URSULAS +from tests.utilities import EVENT_LOOP -def test_bob_can_follow_treasure_map(): +def test_bob_can_follow_treasure_map(enacted_policy_group, ursulas): """ Upon receiving a TreasureMap, Bob populates his list of Ursulas with the correct number. """ - assert len(BOB._ursulas) == 0 - _treasure_map_as_set_on_network, _signature, policy_group = test_alice_sets_treasure_map_on_network() - BOB.follow_treasure_map(policy_group.treasure_map) - assert len(BOB._ursulas) == len(URSULAS) + alice = enacted_policy_group.alice + bob = enacted_policy_group.bob + assert len(bob._ursulas) == 0 + + setter, encrypted_treasure_map, packed_encrypted_treasure_map, signature_for_bob, signature_for_ursula = alice.publish_treasure_map( + enacted_policy_group) + _set_event = EVENT_LOOP.run_until_complete(setter) + + bob.follow_treasure_map(enacted_policy_group.treasure_map) + assert len(bob._ursulas) == len(ursulas) diff --git a/tests/test_utilities.py b/tests/utilities.py similarity index 57% rename from tests/test_utilities.py rename to tests/utilities.py index b3b4256e3..a177230ed 100644 --- a/tests/test_utilities.py +++ b/tests/utilities.py @@ -1,21 +1,38 @@ import asyncio -from nkms.characters import Ursula +from apistar.test import TestClient +from sqlalchemy.engine import create_engine + +from nkms.characters import Ursula, Alice, Bob +from nkms.keystore import keystore +from nkms.keystore.db import Base from nkms.network.node import NetworkyStuff -def make_fake_ursulas(how_many_ursulas: int, ursula_starting_port: int) -> list: + +NUMBER_OF_URSULAS_IN_NETWORK = 6 + +EVENT_LOOP = asyncio.get_event_loop() +asyncio.set_event_loop(EVENT_LOOP) + +URSULA_PORT = 7468 +NUMBER_OF_URSULAS_IN_NETWORK = 6 + + +def make_ursulas(how_many_ursulas: int, ursula_starting_port: int) -> list: """ - :param how_many: How many Ursulas to create. + :param how_many_ursulas: How many Ursulas to create. :param ursula_starting_port: The port of the first created Ursula; subsequent Ursulas will increment the port number by 1. :return: A list of created Ursulas """ - event_loop = asyncio.get_event_loop() URSULAS = [] for _u in range(how_many_ursulas): - _URSULA = Ursula() + engine = create_engine('sqlite:///:memory:') + Base.metadata.create_all(engine) + ursulas_keystore = keystore.KeyStore(engine) + _URSULA = Ursula(urulsas_keystore=ursulas_keystore) _URSULA.attach_server() _URSULA.listen(ursula_starting_port + _u, "127.0.0.1") @@ -26,7 +43,7 @@ def make_fake_ursulas(how_many_ursulas: int, ursula_starting_port: int) -> list: ursula.server.bootstrap([("127.0.0.1", ursula_starting_port + _c) for _c in range(how_many_ursulas)])) ursula.publish_interface_information() - return URSULAS, range(ursula_starting_port, ursula_starting_port + len(URSULAS)) + return URSULAS # , range(ursula_starting_port, ursula_starting_port + len(URSULAS)) class MockPolicyOfferResponse(object): @@ -34,7 +51,6 @@ class MockPolicyOfferResponse(object): class MockNetworkyStuff(NetworkyStuff): - def __init__(self, ursulas): self.ursulas = iter(ursulas) @@ -50,5 +66,7 @@ class MockNetworkyStuff(NetworkyStuff): else: return super().find_ursula(id) - def animate_policy(self, ursula, payload): + def enact_policy(self, ursula, hrac, payload): + mock_client = TestClient(ursula.rest_app) + response = mock_client.post('http://localhost/kFrag/{}'.format(hrac.hex()), payload) return True, ursula.interface_dht_key()