Merge pull request #130 from jMyles/mypy

Various type hints and other mypy-inspired fixes.
pull/131/head
Justin Holmes 2017-11-27 13:35:11 -08:00 committed by GitHub
commit f106235a65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1138 additions and 342 deletions

12
Pipfile
View File

@ -1,8 +1,10 @@
[[source]] [[source]]
url = "https://pypi.python.org/simple" url = "https://pypi.python.org/simple"
verify_ssl = true verify_ssl = true
name = "pypi" name = "pypi"
[packages] [packages]
rpcudp = {git = "https://github.com/nucypher/rpcudp", ref = "kms-dependency"} rpcudp = {git = "https://github.com/nucypher/rpcudp", ref = "kms-dependency"}
@ -12,17 +14,21 @@ lmdb = "*"
pynacl = "*" pynacl = "*"
"pysha3" = "*" "pysha3" = "*"
bidict = "*" bidict = "*"
"py-ecc" = "*" py-ecc = "*"
sqlalchemy = "*" sqlalchemy = "*"
twisted = "*" twisted = "*"
pyopenssl = "*" pyopenssl = "*"
"service-identity" = "*" service-identity = "*"
apistar = "*"
mypy = "*"
pytest-mypy = "*"
[dev-packages] [dev-packages]
pytest = "*" pytest = "*"
coverage = "*" coverage = "*"
"pytest-cov" = "*" pytest-cov = "*"
pdbpp = "*" pdbpp = "*"
ipython = "*" ipython = "*"
appdirs = "*" appdirs = "*"

695
Pipfile.lock generated Normal file
View File

@ -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"
}
}
}

2
mypy.ini Normal file
View File

@ -0,0 +1,2 @@
[mypy]
ignore_missing_imports=True

View File

@ -1,6 +1,12 @@
import asyncio import asyncio
import binascii
from logging import getLogger
import msgpack 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.network import Server
from kademlia.utils import digest 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.api import secure_random, keccak_digest
from nkms.crypto.constants import NOT_SIGNED, NO_DECRYPTION_PERFORMED from nkms.crypto.constants import NOT_SIGNED, NO_DECRYPTION_PERFORMED
from nkms.crypto.powers import CryptoPower, SigningPower, EncryptingPower 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.keystore.keypairs import Keypair
from nkms.network import blockchain_client 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.protocols import dht_value_splitter
from nkms.network.server import NuCypherDHTServer, NuCypherSeedOnlyDHTServer from nkms.network.server import NuCypherDHTServer, NuCypherSeedOnlyDHTServer
from nkms.policy.constants import NOT_FROM_ALICE from nkms.policy.constants import NOT_FROM_ALICE
@ -25,11 +32,8 @@ class Character(object):
_default_crypto_powerups = None _default_crypto_powerups = None
_seal = 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, 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 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. :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 If neither crypto_power nor crypto_power_ups are provided, we give this Character all CryptoPowerUps
listed in their _default_crypto_powerups attribute. 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: if crypto_power and crypto_power_ups:
raise ValueError("Pass crypto_power or crypto_power_ups (or neither), but not both.") raise ValueError("Pass crypto_power or crypto_power_ups (or neither), but not both.")
@ -59,6 +70,13 @@ class Character(object):
else: else:
self._seal = StrangerSeal(self) 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, def attach_server(self, ksize=20, alpha=3, id=None, storage=None,
*args, **kwargs) -> None: *args, **kwargs) -> None:
self._server = self._server_class(ksize, alpha, id, storage, *args, **kwargs) self._server = self._server_class(ksize, alpha, id, storage, *args, **kwargs)
@ -87,7 +105,7 @@ class Character(object):
def learn_about_actor(self, actor): def learn_about_actor(self, actor):
self._actor_mapping[actor.id()] = 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: sign_cleartext=True) -> tuple:
""" """
Looks up recipient actor, finds that actor's pubkey_enc on our keyring, and encrypts for them. 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) actor = self._lookup_actor(recipient)
ciphertext = self._crypto_power.encrypt_for(actor.public_key(EncryptingPower),
cleartext)
if sign: if sign:
if sign_cleartext: if sign_cleartext:
signature = self.seal(cleartext) signature = self.seal(cleartext)
ciphertext = self._crypto_power.encrypt_for(actor.public_key(EncryptingPower),
signature + cleartext)
else: else:
ciphertext = self._crypto_power.encrypt_for(actor.public_key(EncryptingPower),
cleartext)
signature = self.seal(ciphertext) signature = self.seal(ciphertext)
else: else:
signature = NOT_SIGNED signature = NOT_SIGNED
ciphertext = self._crypto_power.encrypt_for(actor.public_key(EncryptingPower),
cleartext)
return ciphertext, signature return ciphertext, signature
def verify_from(self, actor_whom_sender_claims_to_be: "Character", signature: bytes, def verify_from(self, actor_whom_sender_claims_to_be: "Character", message: bytes, signature: Signature = None,
message: bytes, decrypt=False, decrypt=False,
signature_is_on_cleartext=False) -> tuple: signature_is_on_cleartext=False) -> tuple:
""" """
Inverse of encrypt_for. 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. :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) :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 cleartext = NO_DECRYPTION_PERFORMED
if signature_is_on_cleartext: if signature_is_on_cleartext:
if decrypt: if decrypt:
cleartext = self._crypto_power.decrypt(message) cleartext = self._crypto_power.decrypt(message)
message = cleartext signature, message = BytestringSplitter(Signature)(cleartext, return_remainder=True)
else: else:
raise ValueError( raise ValueError(
"Can't look for a signature on the cleartext if we're not decrypting.") "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) :return: Tuple(kfrags, eph_key_data)
""" """
kfrags, eph_key_data = API.ecies_ephemeral_split_rekey( 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) return (kfrags, eph_key_data)
def publish_treasure_map(self, policy_group): 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, self._ursulas[ursula_interface_id] = Ursula.as_discovered_on_network(port=port, interface=interface,
pubkey_sig_bytes=ursula_pubkey_sig) 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() 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) packed_encrypted_treasure_map = event_loop.run_until_complete(ursula_coro)
_signature_for_ursula, pubkey_sig_alice, hrac, encrypted_treasure_map = dht_value_splitter( _signature_for_ursula, pubkey_sig_alice, hrac, encrypted_treasure_map = dht_value_splitter(
packed_encrypted_treasure_map[5::], msgpack_remainder=True) 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) signature_is_on_cleartext=True, decrypt=True)
alices_signature, packed_node_list = BytestringSplitter(Signature)(cleartext, return_remainder=True)
if not verified: if not verified:
return NOT_FROM_ALICE return NOT_FROM_ALICE
else: else:
@ -254,9 +281,23 @@ class Ursula(Character):
interface = None interface = None
interface_ttl = 0 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 @staticmethod
def as_discovered_on_network(port, interface, pubkey_sig_bytes): 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.port = port
ursula.interface = interface ursula.interface = interface
return ursula return ursula
@ -269,6 +310,12 @@ class Ursula(Character):
super().attach_server(ksize, alpha, id, storage) 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): def listen(self, port, interface):
self.port = port self.port = port
self.interface = interface self.interface = interface
@ -298,6 +345,24 @@ class Ursula(Character):
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
loop.run_until_complete(setter) 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): class Seal(object):
""" """
@ -331,6 +396,9 @@ class Seal(object):
def __len__(self): def __len__(self):
return len(bytes(self)) return len(bytes(self))
def without_metabytes(self):
return self.character._crypto_power.pubkey_sig_bytes().without_metabytes()
class StrangerSeal(Seal): class StrangerSeal(Seal):
""" """

View File

@ -4,8 +4,9 @@ from typing import Tuple, Union, List
import sha3 import sha3
from nacl.secret import SecretBox from nacl.secret import SecretBox
from py_ecc.secp256k1 import N, privtopub, ecdsa_raw_recover, ecdsa_raw_sign 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 elliptic_curve
from npre import umbral from npre import umbral
@ -14,7 +15,7 @@ SYSTEM_RAND = SystemRandom()
def secure_random( def secure_random(
num_bytes: int num_bytes: int
) -> bytes: ) -> bytes:
""" """
Returns an amount `num_bytes` of data from the OS's random device. Returns an amount `num_bytes` of data from the OS's random device.
@ -27,13 +28,13 @@ def secure_random(
:return: bytes :return: bytes
""" """
# TODO: Should we just use os.urandom or avoid the import w/ this? # TODO: Should we just use os.urandom or avoid the import w/ this?
return SYSTEM_RAND.getrandbits(num_bytes*8).to_bytes(num_bytes, return SYSTEM_RAND.getrandbits(num_bytes * 8).to_bytes(num_bytes,
byteorder='big') byteorder='big')
def secure_random_range( def secure_random_range(
min: int, min: int,
max: int max: int
) -> int: ) -> int:
""" """
Returns a number from a secure random source betwee the range of Returns a number from a secure random source betwee the range of
@ -48,7 +49,7 @@ def secure_random_range(
def keccak_digest( def keccak_digest(
*messages: bytes *messages: bytes
) -> bytes: ) -> bytes:
""" """
Accepts an iterable containing bytes and digests it returning a Accepts an iterable containing bytes and digests it returning a
@ -66,7 +67,7 @@ def keccak_digest(
def ecdsa_pub2bytes( def ecdsa_pub2bytes(
pubkey: Tuple[int] pubkey: Tuple[int, int]
) -> bytes: ) -> bytes:
""" """
Takes an ECDSA public key and converts to 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') x = pubkey[0].to_bytes(32, byteorder='big')
y = pubkey[1].to_bytes(32, byteorder='big') y = pubkey[1].to_bytes(32, byteorder='big')
return x+y return x + y
def ecdsa_bytes2pub( def ecdsa_bytes2pub(
pubkey: bytes pubkey: bytes
) -> Tuple[int]: ) -> Tuple[int, int]:
""" """
Takes a byte encoded ECDSA public key and converts to a Tuple of x, and y 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 :return: Byte encoded ECDSA privkey
""" """
privkey = secure_random_range(1, N) 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( def ecdsa_priv2pub(
privkey: bytes, privkey: bytes,
to_bytes: bool = True to_bytes: bool = True
) -> Union[bytes, Tuple[int]]: ) -> Union[bytes, Tuple[int]]:
""" """
Returns the public component of an ECDSA private key. Returns the public component of an ECDSA private key.
@ -119,14 +120,14 @@ def ecdsa_priv2pub(
""" """
pubkey = privtopub(privkey) pubkey = privtopub(privkey)
if to_bytes: if to_bytes:
return ecdsa_pub2bytes(pubkey) return SIG_KEYPAIR_BYTE + PUB_KEY_BYTE + ecdsa_pub2bytes(pubkey)
return pubkey return pubkey
def ecdsa_gen_sig( def ecdsa_gen_sig(
v: int, v: int,
r: int, r: int,
s: int s: int
) -> bytes: ) -> bytes:
""" """
Generates an ECDSA signature, in bytes. Generates an ECDSA signature, in bytes.
@ -137,15 +138,15 @@ def ecdsa_gen_sig(
:return: bytestring of v, r, and s :return: bytestring of v, r, and s
""" """
v = v.to_bytes(1, byteorder='big') _v = v.to_bytes(1, byteorder='big')
r = r.to_bytes(32, byteorder='big') _r = r.to_bytes(32, byteorder='big')
s = s.to_bytes(32, byteorder='big') _s = s.to_bytes(32, byteorder='big')
return v+r+s return _v + _r + _s
def ecdsa_load_sig( def ecdsa_load_sig(
signature: bytes signature: bytes
) -> Tuple[int]: ) -> Tuple[int, int, int]:
""" """
Loads an ECDSA signature, from a bytestring, to a tuple. Loads an ECDSA signature, from a bytestring, to a tuple.
@ -160,9 +161,9 @@ def ecdsa_load_sig(
def ecdsa_sign( def ecdsa_sign(
msghash: bytes, msghash: bytes,
privkey: bytes privkey: bytes
) -> Tuple[int]: ) -> Tuple[int, int, int]:
""" """
Accepts a hashed message and signs it with the private key given. Accepts a hashed message and signs it with the private key given.
@ -176,11 +177,11 @@ def ecdsa_sign(
def ecdsa_verify( def ecdsa_verify(
v: int, v: int,
r: int, r: int,
s: int, s: int,
msghash: bytes, msghash: bytes,
pubkey: Union[bytes, Tuple[int]] pubkey: Union[bytes, Tuple[int, int]]
) -> bool: ) -> bool:
""" """
Takes a v, r, s, a pubkey, and a hash of a message to verify via ECDSA. Takes a v, r, s, a pubkey, and a hash of a message to verify via ECDSA.

View File

@ -1,5 +1,4 @@
# TODO: Turn these into classes? # TODO: Turn these into classes?
PUBKEY_SIG_LENGTH = 64
HASH_DIGEST_LENGTH = 32 HASH_DIGEST_LENGTH = 32
NOT_SIGNED = 445 NOT_SIGNED = 445

View File

@ -13,7 +13,7 @@ _tl.pre = None
class EncryptingKeypair(object): class EncryptingKeypair(object):
KEYSIZE = 32 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) :privkey: Optional private key in a serialized form (32-byte string)
If not given, a random one is generated. If not given, a random one is generated.

View File

@ -1,5 +1,5 @@
import inspect 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 import api as API
from nkms.crypto.signature import Signature from nkms.crypto.signature import Signature
@ -104,9 +104,9 @@ class CryptoPowerUp(object):
class KeyPairBasedPower(CryptoPowerUp): 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: if keypair and pubkey_bytes:
raise ValueError("Pass keypair or pubkey_bytes (or neither), but not both.") raise ValueError("Pass keypair or pubkey_bytes (or neither), but not both.")
elif keypair: elif keypair:

View File

@ -1,4 +1,5 @@
from nkms.crypto import api as API from nkms.crypto import api as API
from nkms.keystore.keypairs import PublicKey
class Signature(bytes): class Signature(bytes):
@ -34,7 +35,7 @@ class Signature(bytes):
def __repr__(self): def __repr__(self):
return "{} v{}: {} - {}".format(__class__.__name__, self._v, self._r, self._s) 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. Verifies that a message's signature was valid.
@ -43,8 +44,10 @@ class Signature(bytes):
:return: True if valid, False if invalid :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) 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): def __bytes__(self):
""" """

View File

@ -1,8 +1,8 @@
ENC_KEYPAIR_BYTE = b'E' ENC_KEYPAIR_BYTE = b'E'
SIG_KEYPAIR_BYTE = b'S' SIG_KEYPAIR_BYTE = b'S'
REKEY_FRAG_ID_LEN = 32 REKEY_FRAG_ID_LEN = 33
REKEY_FRAG_KEY_LEN = 32 REKEY_FRAG_KEY_LEN = 33
PUB_KEY_BYTE = b'0' PUB_KEY_BYTE = b'0'
PRIV_KEY_BYTE = b'1' PRIV_KEY_BYTE = b'1'

View File

@ -15,7 +15,7 @@ class Keypair(object):
# TODO: Throw error if a key is called and it doesn't exist # TODO: Throw error if a key is called and it doesn't exist
# TODO: Maybe write a custome error ofr ^? # 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: if privkey and pubkey:
self.privkey, self.pubkey = privkey, pubkey self.privkey, self.pubkey = privkey, pubkey
elif not privkey and not pubkey: elif not privkey and not pubkey:
@ -27,7 +27,7 @@ class Keypair(object):
self._gen_pubkey() self._gen_pubkey()
elif pubkey and not privkey: elif pubkey and not privkey:
# We have only the pubkey; this is a public-only pair. # We have only the pubkey; this is a public-only pair.
self.pubkey = pubkey self.pubkey = PublicKey(pubkey)
self.public_only = True self.public_only = True
@staticmethod @staticmethod
@ -52,10 +52,25 @@ class Keypair(object):
elif keypair_byte == constants.SIG_KEYPAIR_BYTE: elif keypair_byte == constants.SIG_KEYPAIR_BYTE:
if key_type_byte == constants.PUB_KEY_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: elif key_type_byte == constants.PRIV_KEY_BYTE:
return SigningKeypair(privkey=key) 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): class EncryptingKeypair(Keypair):
@ -152,7 +167,7 @@ class SigningKeypair(Keypair):
self._gen_pubkey() self._gen_pubkey()
def _gen_pubkey(self): 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: def sign(self, msghash: bytes) -> bytes:
""" """
@ -175,7 +190,7 @@ class SigningKeypair(Keypair):
:return: Boolean if the signature is valid :return: Boolean if the signature is valid
""" """
v, r, s = API.ecdsa_load_sig(signature) 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: def serialize_pubkey(self) -> bytes:
""" """
@ -198,3 +213,11 @@ class SigningKeypair(Keypair):
constants.PRIV_KEY_BYTE + constants.PRIV_KEY_BYTE +
self.privkey) self.privkey)
return serialized_key return serialized_key
class PublicKey(bytes):
_EXPECTED_LENGTH = 66
_METABYTES_LENGTH = 2
def without_metabytes(self):
return self[self._METABYTES_LENGTH::]

View File

@ -146,7 +146,7 @@ class KeyStore(object):
kfrag_data = b'' kfrag_data = b''
if sig: if sig:
kfrag_data += sig kfrag_data += sig
kfrag_data += kfrag.id + kfrag.key kfrag_data += bytes(kfrag)
kfrag = KeyFrag(hrac, kfrag_data) kfrag = KeyFrag(hrac, kfrag_data)
self.session.add(kfrag) self.session.add(kfrag)

View File

@ -1,4 +1 @@
_ursulas_on_blockchain = [] _ursulas_on_blockchain = []
def list_all_ursulas():
return _ursulas_on_blockchain

View File

@ -1,17 +1,16 @@
import msgpack
from kademlia.node import Node from kademlia.node import Node
from kademlia.protocol import KademliaProtocol from kademlia.protocol import KademliaProtocol
from kademlia.utils import digest from kademlia.utils import digest
from nkms.crypto.api import keccak_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.signature import Signature
from nkms.crypto.utils import BytestringSplitter from nkms.crypto.utils import BytestringSplitter
from nkms.keystore.keypairs import PublicKey
from nkms.network.constants import NODE_HAS_NO_STORAGE from nkms.network.constants import NODE_HAS_NO_STORAGE
from nkms.network.node import NuCypherNode from nkms.network.node import NuCypherNode
from nkms.network.routing import NuCypherRoutingTable 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): 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): 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))) 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. verified = signature.verify(hrac, sender_pubkey_sig)
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)
if not verified or not proper_key == dht_key: if not verified or not proper_key == dht_key:
self.log.warning("Got request to store illegal k/v: {} / {}".format(dht_key, dht_value)) self.log.warning("Got request to store illegal k/v: {} / {}".format(dht_key, dht_value))

View File

@ -1,10 +1,18 @@
import msgpack import msgpack
from npre.constants import UNKNOWN_KFRAG
from nkms.characters import Alice, Bob, Ursula from nkms.characters import Alice, Bob, Ursula
from nkms.crypto import api from nkms.crypto import api
from nkms.crypto.api import keccak_digest 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.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): class PolicyOffer(object):
@ -33,7 +41,7 @@ class PolicyManager(object):
class PolicyManagerForAlice(PolicyManager): class PolicyManagerForAlice(PolicyManager):
def __init__(self, owner: Alice): def __init__(self, owner: Alice) -> None:
self.owner = owner self.owner = owner
def create_policy_group(self, def create_policy_group(self,
@ -54,6 +62,7 @@ class PolicyManagerForAlice(PolicyManager):
for kfrag_id, rekey in enumerate(re_enc_keys): for kfrag_id, rekey in enumerate(re_enc_keys):
policy = Policy.from_alice( policy = Policy.from_alice(
alice=self.owner, alice=self.owner,
bob=bob,
kfrag=rekey, kfrag=rekey,
) )
policies.append(policy) policies.append(policy)
@ -68,7 +77,7 @@ class PolicyGroup(object):
_id = None _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.policies = policies or []
self.alice = alice self.alice = alice
self.bob = bob self.bob = bob
@ -82,11 +91,9 @@ class PolicyGroup(object):
def hash(self, message): def hash(self, message):
return keccak_digest(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. :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: for policy in self.policies:
try: try:
@ -121,12 +128,15 @@ class PolicyGroup(object):
""" """
return self.hash(bytes(self.alice.seal) + self.hrac()) 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: for policy in self.policies:
payload = policy.encrypt_payload_for_ursula() policy_payload = policy.encrypt_payload_for_ursula()
_response = networky_stuff.animate_policy(policy.ursula, full_payload = self.alice.seal + msgpack.dumps(policy_payload)
payload) # TODO: Parse response for confirmation and update TreasureMap with new Ursula friend. response = networky_stuff.enact_policy(policy.ursula,
self.hrac(),
full_payload) # TODO: Parse response for confirmation.
# Assuming response is what we hope for # Assuming response is what we hope for
self.treasure_map.add_ursula(policy.ursula) self.treasure_map.add_ursula(policy.ursula)
@ -152,9 +162,9 @@ class Policy(object):
hashed_part = None hashed_part = None
_id = 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: :param kfrag:
The kFrag obviously, but defaults to UNKNOWN_KFRAG in case the user wants to set it later. 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. :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. :param challenge_size: The number of challenges to create in the ChallengePack.
""" """
self.alice = alice self.alice = alice
self.bob = bob
self.alices_signature = alices_signature
self.kfrag = kfrag self.kfrag = kfrag
self.random_id_portion = api.secure_random(32) # TOOD: Where do we actually want this to live? self.random_id_portion = api.secure_random(32) # TOOD: Where do we actually want this to live?
self.challenge_size = challenge_size self.challenge_size = challenge_size
self.treasure_map = [] self.treasure_map = []
self.challenge_pack = []
self._encrypted_challenge_pack = encrypted_challenge_pack
@property @property
def id(self): def id(self):
@ -190,19 +205,55 @@ class Policy(object):
@staticmethod @staticmethod
def from_alice(kfrag, def from_alice(kfrag,
alice, alice,
bob,
): ):
policy = Policy(alice, kfrag) policy = Policy(alice, bob, kfrag)
policy.generate_challenge_pack() policy.generate_challenge_pack()
return policy 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): 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): def activate(self, ursula, negotiation_result):
self.ursula = ursula self.ursula = ursula
self.negotiation_result = negotiation_result 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): def generate_challenge_pack(self):
if self.kfrag == UNKNOWN_KFRAG: if self.kfrag == UNKNOWN_KFRAG:
# TODO: Test this branch # TODO: Test this branch
@ -211,15 +262,15 @@ class Policy(object):
# TODO: make this work instead of being random. See #46. # TODO: make this work instead of being random. See #46.
import random import random
self.challenge_pack = [(random.getrandbits(32), random.getrandbits(32)) for x in self._challenge_pack = [(random.getrandbits(32), random.getrandbits(32)) for x in
range(self.challenge_size)] range(self.challenge_size)]
return True return True
def encrypt_payload_for_ursula(self): def encrypt_payload_for_ursula(self):
""" """
Craft an offer to send to Ursula. 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): class TreasureMap(object):

View File

@ -47,7 +47,7 @@ def test_actor_with_signing_power_can_sign():
# ...or to get the signer's public key for verification purposes. # ...or to get the signer's public key for verification purposes.
sig = api.ecdsa_load_sig(bytes(signature)) 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 assert verification is True
@ -71,7 +71,7 @@ def test_anybody_can_verify():
signature = alice.seal(message) signature = alice.seal(message)
# Our everyman can verify it. # 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 verification is True
assert cleartext is NO_DECRYPTION_PERFORMED 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) can_sign_but_not_encrypt.learn_about_actor(ursula)
# The Character has the message ready... # 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. # But without the proper PowerUp, no encryption happens.
with pytest.raises(NoEncryptingPower) as e_info: 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) ciphertext, signature = can_sign_and_encrypt.encrypt_for(ursula, cleartext, sign=False)
assert signature == NOT_SIGNED assert signature == NOT_SIGNED
assert ciphertext is not None # annnd fail. assert ciphertext is not None

View File

@ -2,7 +2,7 @@ import nkms.db
import shutil import shutil
import os import os
import appdirs import appdirs
from .fixtures import *
def pytest_runtest_setup(item): def pytest_runtest_setup(item):
# Monkey-patching for tests so that we don't overwrite the default db # Monkey-patching for tests so that we don't overwrite the default db

View File

@ -1,10 +1,13 @@
import unittest
import random import random
import unittest
import sha3 import sha3
from nacl.utils import EncryptedMessage from nacl.utils import EncryptedMessage
from npre import umbral
from npre import elliptic_curve as ec
from nkms.crypto import api 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): class TestCrypto(unittest.TestCase):
@ -85,7 +88,7 @@ class TestCrypto(unittest.TestCase):
pubkey_bytes = api.ecdsa_priv2pub(privkey) pubkey_bytes = api.ecdsa_priv2pub(privkey)
self.assertEqual(bytes, type(pubkey_bytes)) 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(tuple, type(pubkey))
self.assertEqual(2, len(pubkey)) self.assertEqual(2, len(pubkey))
self.assertEqual(int, type(pubkey[0])) self.assertEqual(int, type(pubkey[0]))
@ -106,7 +109,7 @@ class TestCrypto(unittest.TestCase):
# Test Serialization # Test Serialization
pubkey = api.ecdsa_priv2pub(privkey) pubkey = api.ecdsa_priv2pub(privkey)
self.assertEqual(bytes, type(pubkey)) self.assertEqual(bytes, type(pubkey))
self.assertEqual(64, len(pubkey)) self.assertEqual(PublicKey._EXPECTED_LENGTH, len(pubkey))
# Test no serialization # Test no serialization
pubkey = api.ecdsa_priv2pub(privkey, to_bytes=False) pubkey = api.ecdsa_priv2pub(privkey, to_bytes=False)
@ -271,7 +274,7 @@ class TestCrypto(unittest.TestCase):
# Check no serialization # Check no serialization
rekey = api.ecies_rekey(self.privkey_a_bytes, self.privkey_b_bytes, 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(umbral.RekeyFrag, type(rekey))
self.assertEqual(ec.ec_element, type(rekey.key)) self.assertEqual(ec.ec_element, type(rekey.key))
@ -283,7 +286,7 @@ class TestCrypto(unittest.TestCase):
# Check with conversion # Check with conversion
frags = api.ecies_split_rekey(self.privkey_a_bytes, 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(list, type(frags))
self.assertEqual(4, len(frags)) self.assertEqual(4, len(frags))
@ -338,14 +341,10 @@ class TestCrypto(unittest.TestCase):
self.assertEqual(32, len(plain_key)) self.assertEqual(32, len(plain_key))
rk_eb = api.ecies_rekey(eph_priv, self.privkey_b, 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(umbral.RekeyFrag, type(rk_eb))
self.assertEqual(ec.ec_element, type(rk_eb.key)) self.assertEqual(ec.ec_element, type(rk_eb.key))
reenc_key = api.ecies_reencrypt(rk_eb, enc_key) reenc_key = api.ecies_reencrypt(rk_eb, enc_key)
dec_key = api.ecies_decapsulate(self.privkey_b, reenc_key) dec_key = api.ecies_decapsulate(self.privkey_b, reenc_key)
self.assertEqual(plain_key, dec_key) self.assertEqual(plain_key, dec_key)
def test_alpha_is_resolved(self):
with self.assertRaises(ImportError):
from nkms.crypto import _alpha

67
tests/fixtures.py Normal file
View File

@ -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()

View File

@ -1,6 +1,7 @@
import unittest import unittest
from nkms.crypto import api as API from nkms.crypto import api as API
from nkms.keystore import keypairs from nkms.keystore import keypairs
from nkms.keystore.keypairs import PublicKey
class TestKeypairs(unittest.TestCase): class TestKeypairs(unittest.TestCase):
@ -27,8 +28,7 @@ class TestKeypairs(unittest.TestCase):
self.assertEqual(32, len(self.ecdsa_keypair.privkey)) self.assertEqual(32, len(self.ecdsa_keypair.privkey))
self.assertTrue(self.ecdsa_keypair.pubkey is not None) self.assertTrue(self.ecdsa_keypair.pubkey is not None)
self.assertEqual(bytes, type(self.ecdsa_keypair.pubkey)) self.assertEqual(PublicKey._EXPECTED_LENGTH, len(self.ecdsa_keypair.pubkey))
self.assertEqual(64, len(self.ecdsa_keypair.pubkey))
def test_ecdsa_keypair_signing(self): def test_ecdsa_keypair_signing(self):
msghash = API.keccak_digest(b'hello world!') msghash = API.keccak_digest(b'hello world!')
@ -64,8 +64,7 @@ class TestKeypairs(unittest.TestCase):
self.assertEqual(bytes, type(keypair.privkey)) self.assertEqual(bytes, type(keypair.privkey))
self.assertEqual(32, len(keypair.privkey)) self.assertEqual(32, len(keypair.privkey))
self.assertEqual(bytes, type(keypair.pubkey)) self.assertEqual(PublicKey._EXPECTED_LENGTH, len(keypair.pubkey))
self.assertEqual(64, len(keypair.pubkey))
# Test no keys (key generation) # Test no keys (key generation)
keypair = keypairs.SigningKeypair() keypair = keypairs.SigningKeypair()
@ -74,8 +73,7 @@ class TestKeypairs(unittest.TestCase):
self.assertEqual(bytes, type(keypair.privkey)) self.assertEqual(bytes, type(keypair.privkey))
self.assertEqual(32, len(keypair.privkey)) self.assertEqual(32, len(keypair.privkey))
self.assertEqual(bytes, type(keypair.pubkey)) self.assertEqual(PublicKey._EXPECTED_LENGTH, len(keypair.pubkey))
self.assertEqual(64, len(keypair.pubkey))
# Test privkey only # Test privkey only
keypair = keypairs.SigningKeypair(privkey=self.ecdsa_keypair.privkey) keypair = keypairs.SigningKeypair(privkey=self.ecdsa_keypair.privkey)
@ -83,13 +81,10 @@ class TestKeypairs(unittest.TestCase):
self.assertEqual(bytes, type(keypair.privkey)) self.assertEqual(bytes, type(keypair.privkey))
self.assertEqual(32, len(keypair.privkey)) self.assertEqual(32, len(keypair.privkey))
self.assertEqual(PublicKey._EXPECTED_LENGTH, len(keypair.pubkey))
self.assertEqual(bytes, type(keypair.pubkey))
self.assertEqual(64, len(keypair.pubkey))
# Test pubkey only # Test pubkey only
keypair = keypairs.SigningKeypair(pubkey=self.ecdsa_keypair.pubkey) keypair = keypairs.SigningKeypair(pubkey=self.ecdsa_keypair.pubkey)
self.assertTrue(keypair.public_only is True) self.assertTrue(keypair.public_only is True)
self.assertEqual(bytes, type(keypair.pubkey)) self.assertEqual(PublicKey._EXPECTED_LENGTH, len(keypair.pubkey))
self.assertEqual(64, len(keypair.pubkey))

View File

@ -24,7 +24,6 @@ class TestKeyStore(unittest.TestCase):
keypair = self.ks.generate_signing_keypair() keypair = self.ks.generate_signing_keypair()
self.assertEqual(keypairs.SigningKeypair, type(keypair)) self.assertEqual(keypairs.SigningKeypair, type(keypair))
self.assertEqual(bytes, type(keypair.privkey)) self.assertEqual(bytes, type(keypair.privkey))
self.assertEqual(bytes, type(keypair.pubkey))
def test_key_sqlite_keystore(self): def test_key_sqlite_keystore(self):
keypair = self.ks.generate_encrypting_keypair() keypair = self.ks.generate_encrypting_keypair()
@ -74,11 +73,11 @@ class TestKeyStore(unittest.TestCase):
def test_keyfrag_sqlite(self): def test_keyfrag_sqlite(self):
rand_sig = API.secure_random(65) rand_sig = API.secure_random(65)
rand_id = API.secure_random(32) rand_id = b'\x00' + API.secure_random(32)
rand_key = API.secure_random(32) rand_key = b'\x00' + API.secure_random(32)
rand_hrac = 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) self.ks.add_kfrag(rand_hrac, kfrag, sig=rand_sig)
# Check that kfrag was added # Check that kfrag was added

View File

@ -1,61 +1,38 @@
import asyncio import asyncio
import datetime
import msgpack import msgpack
import pytest import pytest
from kademlia.utils import digest from kademlia.utils import digest
from nkms.characters import Ursula, Alice, Character, Bob, congregate from nkms.characters import Ursula, Character
from nkms.crypto.constants import PUBKEY_SIG_LENGTH, HASH_DIGEST_LENGTH
from nkms.crypto.signature import Signature from nkms.crypto.signature import Signature
from nkms.crypto.utils import BytestringSplitter 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.network.protocols import dht_value_splitter
from nkms.policy.constants import NON_PAYMENT from nkms.policy.models import Policy
from nkms.policy.models import PolicyManagerForAlice, PolicyOffer, Policy from tests.utilities import MockNetworkyStuff, EVENT_LOOP, URSULA_PORT, NUMBER_OF_URSULAS_IN_NETWORK
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])
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. Once launched, all Ursulas know about - and can help locate - all other Ursulas in the network.
""" """
ignorance = [] ignorance = []
for acounter, announcing_ursula in enumerate(list_all_ursulas()): for acounter, announcing_ursula in enumerate(blockchain_client._ursulas_on_blockchain):
for counter, propagating_ursula in enumerate(URSULAS): for counter, propagating_ursula in enumerate(ursulas):
if not digest(announcing_ursula) in propagating_ursula.server.storage: if not digest(announcing_ursula) in propagating_ursula.server.storage:
ignorance.append((counter, acounter)) ignorance.append((counter, acounter))
if ignorance: if ignorance:
pytest.fail(str(["{} didn't know about {}".format(counter, acounter) for counter, acounter in 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 Although Ursulas propagate each other's interface information, as demonstrated above, they do not propagate
interface information for Vladimir, an Evil Ursula. interface information for Vladimir, an Evil Ursula.
""" """
vladimir = URSULAS[0] vladimir = ursulas[0]
ursula = URSULAS[1] ursula = ursulas[1]
# Ursula hasn't seen any illegal keys. # Ursula hasn't seen any illegal keys.
assert ursula.server.protocol.illegal_keys_seen == [] 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 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). 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) networky_stuff = MockNetworkyStuff(ursulas)
policy = Policy(Alice()) policy = Policy(alice, bob)
with pytest.raises(Ursula.NotFound): with pytest.raises(Ursula.NotFound):
policy_offer = policy.encrypt_payload_for_ursula() 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 the test character can't make reference to a character he doesn't know about yet.
""" """
tony_clifton = Character() tony_clifton = Character()
message = b"some_message" message = b"some_message"
signature = ALICE.seal(message) signature = alice.seal(message)
# Tony can't reference Alice... # Tony can't reference Alice...
with pytest.raises(Character.NotFound): 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. # ...before learning about Alice.
tony_clifton.learn_about_actor(ALICE) tony_clifton.learn_about_actor(alice)
verification, NO_DECRYPTION_PERFORMED = tony_clifton.verify_from(ALICE, signature, message) verification, NO_DECRYPTION_PERFORMED = tony_clifton.verify_from(alice, message, signature)
assert verification is True 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. With the help of any Ursula, Alice can find a specific Ursula.
""" """
ursula_index = 1 ursula_index = 1
all_ursulas = list_all_ursulas() all_ursulas = blockchain_client._ursulas_on_blockchain
getter = ALICE.server.get(all_ursulas[ursula_index]) getter = alice.server.get(all_ursulas[ursula_index])
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
value = loop.run_until_complete(getter) 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] port = msgpack.loads(interface_info)[0]
assert port == URSULA_PORT + ursula_index 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. alice = alices_policy_group.alice
resource_id = b"as098duasdlkj213098asf" bob = alices_policy_group.bob
# Alice has a policy in mind; she crafts an offer. assert alices_policy_group.hrac() == alices_policy_group.hash(
n = NUMBER_OF_URSULAS_IN_NETWORK bytes(alice.seal) + bytes(bob.seal) + alice.__resource_id)
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
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) _set_event = EVENT_LOOP.run_until_complete(setter)
treasure_map_as_set_on_network = URSULAS[0].server.storage[ treasure_map_as_set_on_network = ursulas[0].server.storage[
digest(policy_group.treasure_map_dht_key())] digest(enacted_policy_group.treasure_map_dht_key())]
assert treasure_map_as_set_on_network == b"trmap" + packed_encrypted_treasure_map 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. 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" 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()) setter = alice.server.set(illegal_policygroup_id, packed_encrypted_treasure_map)
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)
_set_event = EVENT_LOOP.run_until_complete(setter) _set_event = EVENT_LOOP.run_until_complete(setter)
with pytest.raises(KeyError): 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. 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( _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" 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, encrypted_treasure_map,
decrypt=True, decrypt=True,
signature_is_on_cleartext=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 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 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. 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() bob = enacted_policy_group.bob
networky_stuff = MockNetworkyStuff(URSULAS) networky_stuff = MockNetworkyStuff(ursulas)
# Of course, in the real world, Bob has sufficient information to reconstitute a PolicyGroup, gleaned, we presume, # Of course, in the real world, Bob has sufficient information to reconstitute a PolicyGroup, gleaned, we presume,
# through a side-channel with Alice. # 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. 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() alice = enacted_policy_group.alice
for ursula_interface_id in enacted_policy_group.treasure_map:
for ursula_interface_id in policy_group.treasure_map: getter = alice.server.get(ursula_interface_id)
getter = ALICE.server.get(ursula_interface_id)
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
value = loop.run_until_complete(getter) 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] 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

View File

@ -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()

View File

@ -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. Upon receiving a TreasureMap, Bob populates his list of Ursulas with the correct number.
""" """
assert len(BOB._ursulas) == 0 alice = enacted_policy_group.alice
_treasure_map_as_set_on_network, _signature, policy_group = test_alice_sets_treasure_map_on_network() bob = enacted_policy_group.bob
BOB.follow_treasure_map(policy_group.treasure_map) assert len(bob._ursulas) == 0
assert len(BOB._ursulas) == len(URSULAS)
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)

View File

@ -1,21 +1,38 @@
import asyncio 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 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. :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 :return: A list of created Ursulas
""" """
event_loop = asyncio.get_event_loop() event_loop = asyncio.get_event_loop()
URSULAS = [] URSULAS = []
for _u in range(how_many_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.attach_server()
_URSULA.listen(ursula_starting_port + _u, "127.0.0.1") _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.server.bootstrap([("127.0.0.1", ursula_starting_port + _c) for _c in range(how_many_ursulas)]))
ursula.publish_interface_information() 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): class MockPolicyOfferResponse(object):
@ -34,7 +51,6 @@ class MockPolicyOfferResponse(object):
class MockNetworkyStuff(NetworkyStuff): class MockNetworkyStuff(NetworkyStuff):
def __init__(self, ursulas): def __init__(self, ursulas):
self.ursulas = iter(ursulas) self.ursulas = iter(ursulas)
@ -50,5 +66,7 @@ class MockNetworkyStuff(NetworkyStuff):
else: else:
return super().find_ursula(id) 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() return True, ursula.interface_dht_key()