Merge pull request #270 from fjarri/interfacing-3

Final touches for the API
pull/271/head
Bogdan Opanchuk 2021-06-13 13:27:18 -07:00 committed by GitHub
commit 18e868ceb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 586 additions and 618 deletions

View File

@ -13,23 +13,19 @@ bumpversion = "*"
# Pytest Plugins # Pytest Plugins
pytest = "*" pytest = "*"
pytest-mypy = "*" pytest-mypy = "*"
pytest-mock = "*"
pytest-cov = "*" pytest-cov = "*"
pytest-benchmark = {version = "*",extras = ["histogram"]} pytest-benchmark = {version = "*",extras = ["histogram"]}
# Pytest Plugin Subdeps # Pytest Plugin Subdeps
mock = "*"
mypy = "*" mypy = "*"
monkeytype = "*"
coverage = "*" coverage = "*"
codecov = "*" codecov = "*"
# Testing libraries # Testing libraries
hypothesis = "*"
nbval = "*" nbval = "*"
# Docs # Docs
sphinx = "*" sphinx = "*"
sphinx-autobuild = "*" sphinx-autobuild = "*"
sphinx_rtd_theme = "*" sphinx_rtd_theme = "*"
# Overrides vulnerable versions allowed by codecov and sphinx: # Overrides vulnerable versions allowed by codecov and sphinx:
requests = ">=2.20.0" requests = ">=2.20.0"
[pipenv] [pipenv]

501
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "55db9d5f9de5a65ef3a43b7931e19d8a1ee2cb9cbfa9e616fa2c512f8576116b" "sha256": "5046c797bf7733818d480d0c9df2934393dbd080d847a85d03221cff74ec7907"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": {}, "requires": {},
@ -17,14 +17,23 @@
"cffi": { "cffi": {
"hashes": [ "hashes": [
"sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813", "sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813",
"sha256:04c468b622ed31d408fea2346bec5bbffba2cc44226302a0de1ade9f5ea3d373",
"sha256:06d7cd1abac2ffd92e65c0609661866709b4b2d82dd15f611e602b9b188b0b69",
"sha256:06db6321b7a68b2bd6df96d08a5adadc1fa0e8f419226e25b2a5fbf6ccc7350f",
"sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06", "sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06",
"sha256:0f861a89e0043afec2a51fd177a567005847973be86f709bbb044d7f42fc4e05",
"sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea", "sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea",
"sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee", "sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee",
"sha256:1bf1ac1984eaa7675ca8d5745a8cb87ef7abecb5592178406e55858d411eadc0",
"sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396", "sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396",
"sha256:24a570cd11895b60829e941f2613a4f79df1a27344cbbb82164ef2e0116f09c7",
"sha256:24ec4ff2c5c0c8f9c6b87d5bb53555bf267e1e6f70e52e5a9740d32861d36b6f",
"sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73", "sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73",
"sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315", "sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315",
"sha256:293e7ea41280cb28c6fcaaa0b1aa1f533b8ce060b9e701d78511e1e6c4a1de76",
"sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1", "sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1",
"sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49", "sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49",
"sha256:3c3f39fa737542161d8b0d680df2ec249334cd70a8f420f71c9304bd83c3cbed",
"sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892", "sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892",
"sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482", "sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482",
"sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058", "sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058",
@ -32,6 +41,7 @@
"sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53", "sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53",
"sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045", "sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045",
"sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3", "sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3",
"sha256:681d07b0d1e3c462dd15585ef5e33cb021321588bebd910124ef4f4fb71aef55",
"sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5", "sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5",
"sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e", "sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e",
"sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c", "sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c",
@ -49,8 +59,10 @@
"sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e", "sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e",
"sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991", "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991",
"sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6", "sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6",
"sha256:cc5a8e069b9ebfa22e26d0e6b97d6f9781302fe7f4f2b8776c3e1daea35f1adc",
"sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1", "sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1",
"sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406", "sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406",
"sha256:df5052c5d867c1ea0b311fb7c3cd28b19df469c056f7fdcfe88c7473aa63e333",
"sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d", "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d",
"sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c" "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"
], ],
@ -58,21 +70,21 @@
}, },
"cryptography": { "cryptography": {
"hashes": [ "hashes": [
"sha256:066bc53f052dfeda2f2d7c195cf16fb3e5ff13e1b6b7415b468514b40b381a5b", "sha256:0f1212a66329c80d68aeeb39b8a16d54ef57071bf22ff4e521657b27372e327d",
"sha256:0923ba600d00718d63a3976f23cab19aef10c1765038945628cd9be047ad0336", "sha256:1e056c28420c072c5e3cb36e2b23ee55e260cb04eee08f702e0edfec3fb51959",
"sha256:2d32223e5b0ee02943f32b19245b61a62db83a882f0e76cc564e1cec60d48f87", "sha256:240f5c21aef0b73f40bb9f78d2caff73186700bf1bc6b94285699aff98cc16c6",
"sha256:4169a27b818de4a1860720108b55a2801f32b6ae79e7f99c00d79f2a2822eeb7", "sha256:26965837447f9c82f1855e0bc8bc4fb910240b6e0d16a664bb722df3b5b06873",
"sha256:57ad77d32917bc55299b16d3b996ffa42a1c73c6cfa829b14043c561288d2799", "sha256:37340614f8a5d2fb9aeea67fd159bfe4f5f4ed535b1090ce8ec428b2f15a11f2",
"sha256:5ecf2bcb34d17415e89b546dbb44e73080f747e504273e4d4987630493cded1b", "sha256:3d10de8116d25649631977cb37da6cbdd2d6fa0e0281d014a5b7d337255ca713",
"sha256:600cf9bfe75e96d965509a4c0b2b183f74a4fa6f5331dcb40fb7b77b7c2484df", "sha256:3d8427734c781ea5f1b41d6589c293089704d4759e34597dce91014ac125aad1",
"sha256:66b57a9ca4b3221d51b237094b0303843b914b7d5afd4349970bb26518e350b0", "sha256:7ec5d3b029f5fa2b179325908b9cd93db28ab7b85bb6c1db56b10e0b54235177",
"sha256:93cfe5b7ff006de13e1e89830810ecbd014791b042cbe5eec253be11ac2b28f3", "sha256:8e56e16617872b0957d1c9742a3f94b43533447fd78321514abbe7db216aa250",
"sha256:9e98b452132963678e3ac6c73f7010fe53adf72209a32854d55690acac3f6724", "sha256:de4e5f7f68220d92b7637fc99847475b59154b7a1b3868fb7385337af54ac9ca",
"sha256:df186fcbf86dc1ce56305becb8434e4b6b7504bc724b71ad7a3239e0c9d14ef2", "sha256:eb8cc2afe8b05acbd84a43905832ec78e7b3873fb124ca190f574dca7389a87d",
"sha256:fec7fb46b10da10d9e1d078d1ff8ed9e05ae14f431fdbd11145edd0550b9a964" "sha256:ee77aa129f481be46f8d92a1a7db57269a2f23052d5f2433b4621bb457081cc9"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.4.6" "version": "==3.4.7"
}, },
"pycparser": { "pycparser": {
"hashes": [ "hashes": [
@ -108,11 +120,11 @@
}, },
"six": { "six": {
"hashes": [ "hashes": [
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.15.0" "version": "==1.16.0"
} }
}, },
"develop": { "develop": {
@ -133,19 +145,19 @@
}, },
"attrs": { "attrs": {
"hashes": [ "hashes": [
"sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
"sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==20.3.0" "version": "==21.2.0"
}, },
"babel": { "babel": {
"hashes": [ "hashes": [
"sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5", "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9",
"sha256:da031ab54472314f210b0adcff1588ee5d1d1d0ba4dbd07b94dba82bde791e05" "sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.9.0" "version": "==2.9.1"
}, },
"backcall": { "backcall": {
"hashes": [ "hashes": [
@ -172,10 +184,10 @@
}, },
"certifi": { "certifi": {
"hashes": [ "hashes": [
"sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c", "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee",
"sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830" "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"
], ],
"version": "==2020.12.5" "version": "==2021.5.30"
}, },
"chardet": { "chardet": {
"hashes": [ "hashes": [
@ -262,10 +274,11 @@
}, },
"decorator": { "decorator": {
"hashes": [ "hashes": [
"sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760", "sha256:6e5c199c16f7a9f0e3a61a4a54b3d27e7dad0dbdde92b944426cb20914376323",
"sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7" "sha256:72ecfba4320a893c53f9706bebb2d55c270c1e51a28789361aa93e4a21319ed5"
], ],
"version": "==4.4.2" "markers": "python_version >= '3.5'",
"version": "==5.0.9"
}, },
"docutils": { "docutils": {
"hashes": [ "hashes": [
@ -282,14 +295,6 @@
], ],
"version": "==3.0.12" "version": "==3.0.12"
}, },
"hypothesis": {
"hashes": [
"sha256:2dd38676402d1c218225210cde0cf19f286352279f32631ac5c801f5d767bc94",
"sha256:3b7d9f7e40e406b550d4fd26fef0ce3fad216f163a3400ab701329b865e25876"
],
"index": "pypi",
"version": "==6.8.1"
},
"idna": { "idna": {
"hashes": [ "hashes": [
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
@ -315,19 +320,19 @@
}, },
"ipykernel": { "ipykernel": {
"hashes": [ "hashes": [
"sha256:98321abefdf0505fb3dc7601f60fc4087364d394bd8fad53107eb1adee9ff475", "sha256:29eee66548ee7c2edb7941de60c0ccf0a7a8dd957341db0a49c5e8e6a0fcb712",
"sha256:efd07253b54d84d26e0878d268c8c3a41582a18750da633c2febfd2ece0d467d" "sha256:e976751336b51082a89fc2099fb7f96ef20f535837c398df6eab1283c2070884"
], ],
"markers": "python_version >= '3.5'", "markers": "python_version >= '3.5'",
"version": "==5.5.0" "version": "==5.5.5"
}, },
"ipython": { "ipython": {
"hashes": [ "hashes": [
"sha256:04323f72d5b85b606330b6d7e2dc8d2683ad46c3905e955aa96ecc7a99388e70", "sha256:a171caa3d3d4c819a1c0742e3abecfd5a2b8ab525ca1c9f114b40b76b0679ab1",
"sha256:34207ffb2f653bced2bc8e3756c1db86e7d93e44ed049daae9814fed66d408ec" "sha256:f86788eef439891438af3498525094cc2acbdbea4f2aa2f8895782d4ff471341"
], ],
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==7.21.0" "version": "==7.24.0"
}, },
"ipython-genutils": { "ipython-genutils": {
"hashes": [ "hashes": [
@ -346,11 +351,11 @@
}, },
"jinja2": { "jinja2": {
"hashes": [ "hashes": [
"sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419", "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4",
"sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6" "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "markers": "python_version >= '3.6'",
"version": "==2.11.3" "version": "==3.0.1"
}, },
"jsonschema": { "jsonschema": {
"hashes": [ "hashes": [
@ -375,14 +380,6 @@
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==4.7.1" "version": "==4.7.1"
}, },
"libcst": {
"hashes": [
"sha256:2766671c107263daa3fc34e39d55134a6fe253701564d7670586f30eee2c201c",
"sha256:4638e4e8f166f4c74df399222d347ce3e1d316e206b550d8a6254d51b4cf7275"
],
"markers": "python_version >= '3.6'",
"version": "==0.3.17"
},
"livereload": { "livereload": {
"hashes": [ "hashes": [
"sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869" "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"
@ -391,77 +388,51 @@
}, },
"markupsafe": { "markupsafe": {
"hashes": [ "hashes": [
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298",
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64",
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b",
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567",
"sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff",
"sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f", "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74",
"sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39", "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35",
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26",
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7",
"sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014", "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75",
"sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f", "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f",
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135",
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8",
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a",
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914",
"sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18",
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8",
"sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2",
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d",
"sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85", "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b",
"sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1", "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f",
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb",
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833",
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415",
"sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850", "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902",
"sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0", "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9",
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d",
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066",
"sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb", "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f",
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5",
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94",
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509",
"sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1", "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51",
"sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2", "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
"sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7",
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
"sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8",
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
"sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193",
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
"sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b",
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
"sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2",
"sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5",
"sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c",
"sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032",
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be",
"sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '3.6'",
"version": "==1.1.1" "version": "==2.0.1"
}, },
"mock": { "matplotlib-inline": {
"hashes": [ "hashes": [
"sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62", "sha256:5cf1176f554abb4fa98cb362aa2b55c500147e4bdbb07e3fda359143e1da0811",
"sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc" "sha256:f41d5ff73c9f5385775d5c0bc13b424535c8402fe70ea8210f93e11f3683993e"
], ],
"index": "pypi", "markers": "python_version >= '3.5'",
"version": "==4.0.3" "version": "==0.1.2"
},
"monkeytype": {
"hashes": [
"sha256:b8ed88485d2ffb05fb1597a6e5eacb05ba5420de682054403c06fac84fdc4038",
"sha256:fe596bebc5e1b6a64eae71a40b880688de433e4f70507a31ada48510195251dd"
],
"index": "pypi",
"version": "==20.5.0"
}, },
"mypy": { "mypy": {
"hashes": [ "hashes": [
@ -500,11 +471,11 @@
}, },
"nbformat": { "nbformat": {
"hashes": [ "hashes": [
"sha256:1d223e64a18bfa7cdf2db2e9ba8a818312fc2a0701d2e910b58df66809385a56", "sha256:b516788ad70771c6250977c1374fcca6edebe6126fd2adb5a69aa5c2356fd1c8",
"sha256:3949fdc8f5fa0b1afca16fb307546e78494fa7a7bceff880df8168eafda0e7ac" "sha256:eb8447edd7127d043361bc17f2f5a807626bc8e878c7709a1c647abda28a9171"
], ],
"markers": "python_version >= '3.5'", "markers": "python_version >= '3.5'",
"version": "==5.1.2" "version": "==5.1.3"
}, },
"nbval": { "nbval": {
"hashes": [ "hashes": [
@ -524,11 +495,11 @@
}, },
"parso": { "parso": {
"hashes": [ "hashes": [
"sha256:15b00182f472319383252c18d5913b69269590616c947747bc50bf4ac768f410", "sha256:12b83492c6239ce32ff5eed6d3639d6a536170723c6f3f1506869f1ace413398",
"sha256:8519430ad07087d4c997fda3a7918f7cfa27cb58972a8c89c2a0295a1c940e9e" "sha256:a8c4922db71e4fdb90e0d0bc6e50f9b273d3397925e5e60a717e719201778d22"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==0.8.1" "version": "==0.8.2"
}, },
"pexpect": { "pexpect": {
"hashes": [ "hashes": [
@ -555,11 +526,11 @@
}, },
"prompt-toolkit": { "prompt-toolkit": {
"hashes": [ "hashes": [
"sha256:4cea7d09e46723885cb8bc54678175453e5071e9449821dce6f017b1d1fbfc1a", "sha256:bf00f22079f5fadc949f42ae8ff7f05702826a97059ffcc6281036ad40ac6f04",
"sha256:9397a7162cf45449147ad6042fa37983a081b8a73363a5253dd4072666333137" "sha256:e1b4f11b9336a28fa11810bc623c357420f69dfdb6d2dac41ca2c21a55c033bc"
], ],
"markers": "python_full_version >= '3.6.1'", "markers": "python_full_version >= '3.6.1'",
"version": "==3.0.17" "version": "==3.0.18"
}, },
"ptyprocess": { "ptyprocess": {
"hashes": [ "hashes": [
@ -578,9 +549,9 @@
}, },
"py-cpuinfo": { "py-cpuinfo": {
"hashes": [ "hashes": [
"sha256:9aa2e49675114959697d25cf57fec41c29b55887bff3bc4809b44ac6f5730097" "sha256:5f269be0e08e33fd959de96b34cd4aeeeacac014dd8305f70eb28d06de2345c5"
], ],
"version": "==7.0.0" "version": "==8.0.0"
}, },
"pygal": { "pygal": {
"hashes": [ "hashes": [
@ -598,11 +569,11 @@
}, },
"pygments": { "pygments": {
"hashes": [ "hashes": [
"sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94", "sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f",
"sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8" "sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e"
], ],
"markers": "python_version >= '3.5'", "markers": "python_version >= '3.5'",
"version": "==2.8.1" "version": "==2.9.0"
}, },
"pyparsing": { "pyparsing": {
"hashes": [ "hashes": [
@ -621,46 +592,38 @@
}, },
"pytest": { "pytest": {
"hashes": [ "hashes": [
"sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9", "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b",
"sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839" "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"
], ],
"index": "pypi", "index": "pypi",
"version": "==6.2.2" "version": "==6.2.4"
}, },
"pytest-benchmark": { "pytest-benchmark": {
"extras": [ "extras": [
"histogram" "histogram"
], ],
"hashes": [ "hashes": [
"sha256:01f79d38d506f5a3a0a9ada22ded714537bbdfc8147a881a35c1655db07289d9", "sha256:36d2b08c4882f6f997fd3126a3d6dfd70f3249cde178ed8bbc0b73db7c20f809",
"sha256:ad4314d093a3089701b24c80a05121994c7765ce373478c8f4ba8d23c9ba9528" "sha256:40e263f912de5a81d891619032983557d62a3d85843f9a9f30b98baea0cd7b47"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.2.3" "version": "==3.4.1"
}, },
"pytest-cov": { "pytest-cov": {
"hashes": [ "hashes": [
"sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7", "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a",
"sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da" "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.11.1" "version": "==2.12.1"
},
"pytest-mock": {
"hashes": [
"sha256:379b391cfad22422ea2e252bdfc008edd08509029bcde3c25b2c0bd741e0424e",
"sha256:a1e2aba6af9560d313c642dae7e00a2a12b022b80301d9d7fc8ec6858e1dd9fc"
],
"index": "pypi",
"version": "==3.5.1"
}, },
"pytest-mypy": { "pytest-mypy": {
"hashes": [ "hashes": [
"sha256:63d418a4fea7d598ac40b659723c00804d16a251d90a5cfbca213eeba5aaf01c", "sha256:1fa55723a4bf1d054fcba1c3bd694215a2a65cc95ab10164f5808afd893f3b11",
"sha256:8d2112972c1debf087943f48963a0daf04f3424840aea0cf437cc97053b1b0ef" "sha256:6e68e8eb7ceeb7d1c83a1590912f784879f037b51adfb9c17b95c6b2fc57466b"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.8.0" "version": "==0.8.1"
}, },
"python-dateutil": { "python-dateutil": {
"hashes": [ "hashes": [
@ -677,78 +640,43 @@
], ],
"version": "==2021.1" "version": "==2021.1"
}, },
"pyyaml": {
"hashes": [
"sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf",
"sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696",
"sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393",
"sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77",
"sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922",
"sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5",
"sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8",
"sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10",
"sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc",
"sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018",
"sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e",
"sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253",
"sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347",
"sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183",
"sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541",
"sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb",
"sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185",
"sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc",
"sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db",
"sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa",
"sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46",
"sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122",
"sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b",
"sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63",
"sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df",
"sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc",
"sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247",
"sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6",
"sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==5.4.1"
},
"pyzmq": { "pyzmq": {
"hashes": [ "hashes": [
"sha256:13465c1ff969cab328bc92f7015ce3843f6e35f8871ad79d236e4fbc85dbe4cb", "sha256:089b974ec04d663b8685ac90e86bfe0e4da9d911ff3cf52cb765ff22408b102d",
"sha256:23a74de4b43c05c3044aeba0d1f3970def8f916151a712a3ac1e5cd9c0bc2902", "sha256:0ea7f4237991b0f745a4432c63e888450840bf8cb6c48b93fb7d62864f455529",
"sha256:26380487eae4034d6c2a3fb8d0f2dff6dd0d9dd711894e8d25aa2d1938950a33", "sha256:0f0f27eaab9ba7b92d73d71c51d1a04464a1da6097a252d007922103253d2313",
"sha256:279cc9b51db48bec2db146f38e336049ac5a59e5f12fb3a8ad864e238c1c62e3", "sha256:12ffcf33db6ba7c0e5aaf901e65517f5e2b719367b80bcbfad692f546a297c7a",
"sha256:2f971431aaebe0a8b54ac018e041c2f0b949a43745444e4dadcc80d0f0ef8457", "sha256:1389b615917d4196962a9b469e947ba862a8ec6f5094a47da5e7a8d404bc07a4",
"sha256:30df70f81fe210506aa354d7fd486a39b87d9f7f24c3d3f4f698ec5d96b8c084", "sha256:18dd2ca4540c476558099891c129e6f94109971d110b549db2a9775c817cedbd",
"sha256:33acd2b9790818b9d00526135acf12790649d8d34b2b04d64558b469c9d86820", "sha256:24fb5bb641f0b2aa25fc3832f4b6fc62430f14a7d328229fe994b2bcdc07c93a",
"sha256:38e3dca75d81bec4f2defa14b0a65b74545812bb519a8e89c8df96bbf4639356", "sha256:285514956c08c7830da9d94e01f5414661a987831bd9f95e4d89cc8aaae8da10",
"sha256:3e29f9cf85a40d521d048b55c63f59d6c772ac1c4bf51cdfc23b62a62e377c33", "sha256:41049cff5265e9cd75606aa2c90a76b9c80b98d8fe70ee08cf4af3cedb113358",
"sha256:3ef50d74469b03725d781a2a03c57537d86847ccde587130fe35caafea8f75c6", "sha256:461ed80d741692d9457ab820b1cc057ba9c37c394e67b647b639f623c8b321f6",
"sha256:4231943514812dfb74f44eadcf85e8dd8cf302b4d0bce450ce1357cac88dbfdc", "sha256:4b8fb1b3174b56fd020e4b10232b1764e52cf7f3babcfb460c5253bdc48adad0",
"sha256:4f34a173f813b38b83f058e267e30465ed64b22cd0cf6bad21148d3fa718f9bb", "sha256:4c4fe69c7dc0d13d4ae180ad650bb900854367f3349d3c16f0569f6c6447f698",
"sha256:532af3e6dddea62d9c49062ece5add998c9823c2419da943cf95589f56737de0", "sha256:4e9b9a2f6944acdaf57316436c1acdcb30b8df76726bcf570ad9342bc5001654",
"sha256:581787c62eaa0e0db6c5413cedc393ebbadac6ddfd22e1cf9a60da23c4f1a4b2", "sha256:6355f81947e1fe6e7bb9e123aeb3067264391d3ebe8402709f824ef8673fa6f3",
"sha256:60e63577b85055e4cc43892fecd877b86695ee3ef12d5d10a3c5d6e77a7cc1a3", "sha256:68be16107f41563b9f67d93dff1c9f5587e0f76aa8fd91dc04c83d813bcdab1f",
"sha256:61e4bb6cd60caf1abcd796c3f48395e22c5b486eeca6f3a8797975c57d94b03e", "sha256:68e2c4505992ab5b89f976f89a9135742b18d60068f761bef994a6805f1cae0c",
"sha256:6d4163704201fff0f3ab0cd5d7a0ea1514ecfffd3926d62ec7e740a04d2012c7", "sha256:7040d6dd85ea65703904d023d7f57fab793d7ffee9ba9e14f3b897f34ff2415d",
"sha256:7026f0353977431fc884abd4ac28268894bd1a780ba84bb266d470b0ec26d2ed", "sha256:734ea6565c71fc2d03d5b8c7d0d7519c96bb5567e0396da1b563c24a4ac66f0c",
"sha256:763c175294d861869f18eb42901d500eda7d3fa4565f160b3b2fd2678ea0ebab", "sha256:9ee48413a2d3cd867fd836737b4c89c24cea1150a37f4856d82d20293fa7519f",
"sha256:81e7df0da456206201e226491aa1fc449da85328bf33bbeec2c03bb3a9f18324", "sha256:a1c77796f395804d6002ff56a6a8168c1f98579896897ad7e35665a9b4a9eec5",
"sha256:9221783dacb419604d5345d0e097bddef4459a9a95322de6c306bf1d9896559f", "sha256:b2f707b52e09098a7770503e39294ca6e22ae5138ffa1dd36248b6436d23d78e",
"sha256:a558c5bc89d56d7253187dccc4e81b5bb0eac5ae9511eb4951910a1245d04622", "sha256:bf80b2cec42d96117248b99d3c86e263a00469c840a778e6cb52d916f4fdf82c",
"sha256:b25e5d339550a850f7e919fe8cb4c8eabe4c917613db48dab3df19bfb9a28969", "sha256:c4674004ed64685a38bee222cd75afa769424ec603f9329f0dd4777138337f48",
"sha256:b62ea18c0458a65ccd5be90f276f7a5a3f26a6dea0066d948ce2fa896051420f", "sha256:c6a81c9e6754465d09a87e3acd74d9bb1f0039b2d785c6899622f0afdb41d760",
"sha256:c0cde362075ee8f3d2b0353b283e203c2200243b5a15d5c5c03b78112a17e7d4", "sha256:c6d0c32532a0519997e1ded767e184ebb8543bdb351f8eff8570bd461e874efc",
"sha256:c5e29fe4678f97ce429f076a2a049a3d0b2660ada8f2c621e5dc9939426056dd", "sha256:c8fff75af4c7af92dce9f81fa2a83ed009c3e1f33ee8b5222db2ef80b94e242e",
"sha256:d18ddc6741b51f3985978f2fda57ddcdae359662d7a6b395bc8ff2292fca14bd", "sha256:cb9f9fe1305ef69b65794655fd89b2209b11bff3e837de981820a8aa051ef914",
"sha256:da7d4d4c778c86b60949d17531e60c54ed3726878de8a7f8a6d6e7f8cc8c3205", "sha256:d3ecfee2ee8d91ab2e08d2d8e89302c729b244e302bbc39c5b5dde42306ff003",
"sha256:f52070871a0fd90a99130babf21f8af192304ec1e995bec2a9533efc21ea4452", "sha256:d5e5be93e1714a59a535bbbc086b9e4fd2448c7547c5288548f6fd86353cad9e",
"sha256:f5831eff6b125992ec65d973f5151c48003b6754030094723ac4c6e80a97c8c4", "sha256:de5806be66c9108e4dcdaced084e8ceae14100aa559e2d57b4f0cceb98c462de",
"sha256:f7f63ce127980d40f3e6a5fdb87abf17ce1a7c2bd8bf2c7560e1bbce8ab1f92d", "sha256:f49755684a963731479ff3035d45a8185545b4c9f662d368bd349c419839886d",
"sha256:ff1ea14075bbddd6f29bf6beb8a46d0db779bcec6b9820909584081ec119f8fd" "sha256:fc712a90401bcbf3fa25747f189d6dcfccbecc32712701cad25c6355589dac57"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==22.0.3" "version": "==22.1.0"
}, },
"requests": { "requests": {
"hashes": [ "hashes": [
@ -760,11 +688,11 @@
}, },
"six": { "six": {
"hashes": [ "hashes": [
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.15.0" "version": "==1.16.0"
}, },
"snowballstemmer": { "snowballstemmer": {
"hashes": [ "hashes": [
@ -773,20 +701,13 @@
], ],
"version": "==2.1.0" "version": "==2.1.0"
}, },
"sortedcontainers": {
"hashes": [
"sha256:37257a32add0a3ee490bb170b599e93095eed89a55da91fa9f48753ea12fd73f",
"sha256:59cc937650cf60d677c16775597c89a960658a09cf7c1a668f86e1e4464b10a1"
],
"version": "==2.3.0"
},
"sphinx": { "sphinx": {
"hashes": [ "hashes": [
"sha256:672cfcc24b6b69235c97c750cb190a44ecd72696b4452acaf75c2d9cc78ca5ff", "sha256:b5c2ae4120bf00c799ba9b3699bc895816d272d120080fbc967292f29b52b48c",
"sha256:ef64a814576f46ec7de06adf11b433a0d6049be007fefe7fd0d183d28b581fac" "sha256:d1cb10bee9c4231f1700ec2e24a91be3f3a3aba066ea4ca9f3bbe47e59d5a1d4"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.5.2" "version": "==4.0.2"
}, },
"sphinx-autobuild": { "sphinx-autobuild": {
"hashes": [ "hashes": [
@ -798,11 +719,11 @@
}, },
"sphinx-rtd-theme": { "sphinx-rtd-theme": {
"hashes": [ "hashes": [
"sha256:eda689eda0c7301a80cf122dad28b1861e5605cbf455558f3775e1e8200e83a5", "sha256:32bd3b5d13dc8186d7a42fc816a23d32e83a4827d7d9882948e7b837c232da5a",
"sha256:fa6bebd5ab9a73da8e102509a86f3fcc36dec04a0b52ea80e5a033b2aba00113" "sha256:4a05bdbe8b1446d77a01e20a23ebc6777c74f43237035e76be89699308987d6f"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.5.1" "version": "==0.5.2"
}, },
"sphinxcontrib-applehelp": { "sphinxcontrib-applehelp": {
"hashes": [ "hashes": [
@ -822,11 +743,11 @@
}, },
"sphinxcontrib-htmlhelp": { "sphinxcontrib-htmlhelp": {
"hashes": [ "hashes": [
"sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f", "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07",
"sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b" "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"
], ],
"markers": "python_version >= '3.5'", "markers": "python_version >= '3.6'",
"version": "==1.0.3" "version": "==2.0.0"
}, },
"sphinxcontrib-jsmath": { "sphinxcontrib-jsmath": {
"hashes": [ "hashes": [
@ -846,11 +767,11 @@
}, },
"sphinxcontrib-serializinghtml": { "sphinxcontrib-serializinghtml": {
"hashes": [ "hashes": [
"sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc", "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd",
"sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a" "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"
], ],
"markers": "python_version >= '3.5'", "markers": "python_version >= '3.5'",
"version": "==1.1.4" "version": "==1.1.5"
}, },
"toml": { "toml": {
"hashes": [ "hashes": [
@ -917,62 +838,54 @@
}, },
"typed-ast": { "typed-ast": {
"hashes": [ "hashes": [
"sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1", "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace",
"sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d", "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff",
"sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6", "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266",
"sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd", "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528",
"sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37", "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6",
"sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151", "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808",
"sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07", "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4",
"sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440", "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363",
"sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70", "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341",
"sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496", "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04",
"sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea", "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41",
"sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400", "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e",
"sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc", "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3",
"sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606", "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899",
"sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc", "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805",
"sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581", "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c",
"sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412", "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c",
"sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a", "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39",
"sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2", "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a",
"sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787", "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3",
"sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f", "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7",
"sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937", "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f",
"sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64", "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075",
"sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487", "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0",
"sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b", "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40",
"sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41", "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428",
"sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a", "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927",
"sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3", "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3",
"sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166", "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f",
"sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10" "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"
], ],
"version": "==1.4.2" "version": "==1.4.3"
}, },
"typing-extensions": { "typing-extensions": {
"hashes": [ "hashes": [
"sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918", "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497",
"sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c", "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342",
"sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f" "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"
], ],
"version": "==3.7.4.3" "version": "==3.10.0.0"
},
"typing-inspect": {
"hashes": [
"sha256:3b98390df4d999a28cf5b35d8b333425af5da2ece8a4ea9e98f71e7591347b4f",
"sha256:8f1b1dd25908dbfd81d3bebc218011531e7ab614ba6e5bf7826d887c834afab7",
"sha256:de08f50a22955ddec353876df7b2545994d6df08a2f45d54ac8c05e530372ca0"
],
"version": "==0.6.0"
}, },
"urllib3": { "urllib3": {
"hashes": [ "hashes": [
"sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df", "sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c",
"sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937" "sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
"version": "==1.26.4" "version": "==1.26.5"
}, },
"wcwidth": { "wcwidth": {
"hashes": [ "hashes": [

View File

@ -147,7 +147,7 @@ and then decrypts the re-encrypted ciphertext.
from umbral import decrypt_reencrypted from umbral import decrypt_reencrypted
bob_cleartext = pre.decrypt_reencrypted(decrypting_sk=bobs_secret_key, bob_cleartext = pre.decrypt_reencrypted(receiving_sk=bobs_secret_key,
delegating_pk=alices_public_key, delegating_pk=alices_public_key,
capsule=capsule, capsule=capsule,
cfrags=cfrags, cfrags=cfrags,

View File

@ -1,6 +1,6 @@
import random import random
from umbral import ( from umbral import (
SecretKey, PublicKey, Signer, GenericError, CapsuleFrag, SecretKey, PublicKey, Signer, CapsuleFrag,
encrypt, generate_kfrags, reencrypt, decrypt_original, decrypt_reencrypted) encrypt, generate_kfrags, reencrypt, decrypt_original, decrypt_reencrypted)
# Generate an Umbral key pair # Generate an Umbral key pair
@ -46,7 +46,7 @@ bob_capsule = capsule
# Attempt Bob's decryption (fail) # Attempt Bob's decryption (fail)
try: try:
fail_decrypted_data = decrypt_original(bobs_secret_key, bob_capsule, ciphertext) fail_decrypted_data = decrypt_original(bobs_secret_key, bob_capsule, ciphertext)
except GenericError: except ValueError:
print("Decryption failed! Bob doesn't has access granted yet.") print("Decryption failed! Bob doesn't has access granted yet.")
# Alice grants access to Bob by generating kfrags # Alice grants access to Bob by generating kfrags
@ -103,7 +103,7 @@ cfrags = [cfrag.verify(capsule,
# ------------------------------------ # ------------------------------------
# Finally, Bob decrypts the re-encrypted ciphertext using his key. # Finally, Bob decrypts the re-encrypted ciphertext using his key.
bob_cleartext = decrypt_reencrypted(decrypting_sk=bobs_secret_key, bob_cleartext = decrypt_reencrypted(receiving_sk=bobs_secret_key,
delegating_pk=alices_public_key, delegating_pk=alices_public_key,
capsule=bob_capsule, capsule=bob_capsule,
verified_cfrags=cfrags, verified_cfrags=cfrags,

View File

@ -94,7 +94,7 @@
"from umbral import decrypt_original\n", "from umbral import decrypt_original\n",
"\n", "\n",
"\n", "\n",
"cleartext = decrypt_original(sk=alices_private_key,\n", "cleartext = decrypt_original(delegating_sk=alices_private_key,\n",
" capsule=capsule,\n", " capsule=capsule,\n",
" ciphertext=ciphertext)\n", " ciphertext=ciphertext)\n",
"print(cleartext)" "print(cleartext)"
@ -141,13 +141,11 @@
} }
], ],
"source": [ "source": [
"from umbral import GenericError\n",
"\n",
"try:\n", "try:\n",
" fail_decrypted_data = decrypt_original(sk=bobs_private_key,\n", " fail_decrypted_data = decrypt_original(delegating_sk=bobs_private_key,\n",
" capsule=capsule,\n", " capsule=capsule,\n",
" ciphertext=ciphertext)\n", " ciphertext=ciphertext)\n",
"except GenericError:\n", "except ValueError:\n",
" print(\"Decryption failed! Bob doesn't has access granted yet.\")\n" " print(\"Decryption failed! Bob doesn't has access granted yet.\")\n"
] ]
}, },
@ -266,7 +264,7 @@
"source": [ "source": [
"from umbral import decrypt_reencrypted\n", "from umbral import decrypt_reencrypted\n",
"\n", "\n",
"bob_cleartext = decrypt_reencrypted(decrypting_sk=bobs_private_key,\n", "bob_cleartext = decrypt_reencrypted(receiving_sk=bobs_private_key,\n",
" delegating_pk=alices_public_key,\n", " delegating_pk=alices_public_key,\n",
" capsule=capsule,\n", " capsule=capsule,\n",
" verified_cfrags=cfrags,\n", " verified_cfrags=cfrags,\n",

View File

@ -40,7 +40,9 @@ Intermediate objects
:show-inheritance: :show-inheritance:
.. autoclass:: VerifiedKeyFrag() .. autoclass:: VerifiedKeyFrag()
:members:
:special-members: __eq__, __hash__ :special-members: __eq__, __hash__
:show-inheritance:
.. autoclass:: CapsuleFrag() .. autoclass:: CapsuleFrag()
:members: :members:
@ -49,6 +51,7 @@ Intermediate objects
.. autoclass:: VerifiedCapsuleFrag() .. autoclass:: VerifiedCapsuleFrag()
:special-members: __eq__, __hash__ :special-members: __eq__, __hash__
:show-inheritance:
Encryption, re-encryption and decryption Encryption, re-encryption and decryption
---------------------------------------- ----------------------------------------
@ -66,12 +69,16 @@ Encryption, re-encryption and decryption
Utilities Utilities
--------- ---------
.. autoclass:: umbral.GenericError
:show-inheritance:
.. autoclass:: umbral.VerificationError .. autoclass:: umbral.VerificationError
:show-inheritance: :show-inheritance:
.. autoclass:: umbral.serializable.HasSerializedSize
:members: serialized_size
.. autoclass:: umbral.serializable.Serializable .. autoclass:: umbral.serializable.Serializable
:members: from_bytes
:special-members: __bytes__ :special-members: __bytes__
:show-inheritance:
.. autoclass:: umbral.serializable.Deserializable
:members: from_bytes
:show-inheritance:

View File

@ -130,12 +130,12 @@ or re-encrypted for him by Ursula, he will not be able to open it.
.. doctest:: capsule_story .. doctest:: capsule_story
>>> fail = decrypt_original(sk=bobs_secret_key, >>> fail = decrypt_original(delegating_sk=bobs_secret_key,
... capsule=capsule, ... capsule=capsule,
... ciphertext=ciphertext) ... ciphertext=ciphertext)
Traceback (most recent call last): Traceback (most recent call last):
... ...
umbral.GenericError ValueError
Ursulas perform re-encryption Ursulas perform re-encryption
@ -196,7 +196,7 @@ Finally, Bob decrypts the re-encrypted ciphertext using his key.
.. doctest:: capsule_story .. doctest:: capsule_story
>>> from umbral import decrypt_reencrypted >>> from umbral import decrypt_reencrypted
>>> cleartext = decrypt_reencrypted(decrypting_sk=bobs_secret_key, >>> cleartext = decrypt_reencrypted(receiving_sk=bobs_secret_key,
... delegating_pk=alices_public_key, ... delegating_pk=alices_public_key,
... capsule=capsule, ... capsule=capsule,
... verified_cfrags=cfrags, ... verified_cfrags=cfrags,

View File

@ -65,13 +65,9 @@ INSTALL_REQUIRES = [
DEV_INSTALL_REQUIRES = [ DEV_INSTALL_REQUIRES = [
'pytest', 'pytest',
'pytest-mypy', 'pytest-mypy',
'pytest-mock',
'pytest-cov', 'pytest-cov',
'mock',
'hypothesis',
'coverage', 'coverage',
'codecov', 'codecov',
'monkeytype',
'nbval', 'nbval',
'mypy', 'mypy',
'bumpversion', 'bumpversion',

View File

@ -5,7 +5,6 @@ from umbral import (
SecretKey, SecretKey,
PublicKey, PublicKey,
Signer, Signer,
GenericError,
encrypt, encrypt,
decrypt_original, decrypt_original,
reencrypt, reencrypt,
@ -29,7 +28,7 @@ def test_capsule_serialization(alices_keys):
capsule.point_e = CurvePoint.random() capsule.point_e = CurvePoint.random()
capsule_bytes = bytes(capsule) capsule_bytes = bytes(capsule)
with pytest.raises(GenericError): with pytest.raises(ValueError):
Capsule.from_bytes(capsule_bytes) Capsule.from_bytes(capsule_bytes)
@ -86,7 +85,7 @@ def test_open_reencrypted(alices_keys, bobs_keys):
capsule.open_reencrypted(receiving_sk, delegating_pk, []) capsule.open_reencrypted(receiving_sk, delegating_pk, [])
# Not enough cfrags # Not enough cfrags
with pytest.raises(GenericError, match="Internal validation failed"): with pytest.raises(ValueError, match="Internal validation failed"):
capsule.open_reencrypted(receiving_sk, delegating_pk, cfrags[:threshold-1]) capsule.open_reencrypted(receiving_sk, delegating_pk, cfrags[:threshold-1])
# Repeating cfrags # Repeating cfrags

View File

@ -1,6 +1,6 @@
import pytest import pytest
from umbral import reencrypt, CapsuleFrag, PublicKey, Capsule, VerificationError from umbral import encrypt, reencrypt, CapsuleFrag, PublicKey, Capsule, VerificationError
from umbral.curve_point import CurvePoint from umbral.curve_point import CurvePoint
@ -8,9 +8,8 @@ def test_cfrag_serialization(verification_keys, capsule, kfrags):
verifying_pk, delegating_pk, receiving_pk = verification_keys verifying_pk, delegating_pk, receiving_pk = verification_keys
metadata = b'This is an example of metadata for re-encryption request'
for kfrag in kfrags: for kfrag in kfrags:
cfrag = reencrypt(capsule, kfrag, metadata=metadata) cfrag = reencrypt(capsule, kfrag)
cfrag_bytes = bytes(cfrag) cfrag_bytes = bytes(cfrag)
new_cfrag = CapsuleFrag.from_bytes(cfrag_bytes) new_cfrag = CapsuleFrag.from_bytes(cfrag_bytes)
@ -19,34 +18,15 @@ def test_cfrag_serialization(verification_keys, capsule, kfrags):
verifying_pk=verifying_pk, verifying_pk=verifying_pk,
delegating_pk=delegating_pk, delegating_pk=delegating_pk,
receiving_pk=receiving_pk, receiving_pk=receiving_pk,
metadata=metadata,
) )
assert verified_cfrag == cfrag assert verified_cfrag == cfrag
# No metadata
with pytest.raises(VerificationError):
new_cfrag.verify(capsule,
verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
)
# Wrong metadata
with pytest.raises(VerificationError):
new_cfrag.verify(capsule,
verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
metadata=b'Not the same metadata',
)
# Wrong delegating key # Wrong delegating key
with pytest.raises(VerificationError): with pytest.raises(VerificationError):
new_cfrag.verify(capsule, new_cfrag.verify(capsule,
verifying_pk=verifying_pk, verifying_pk=verifying_pk,
delegating_pk=receiving_pk, delegating_pk=receiving_pk,
receiving_pk=receiving_pk, receiving_pk=receiving_pk,
metadata=metadata,
) )
# Wrong receiving key # Wrong receiving key
@ -55,7 +35,6 @@ def test_cfrag_serialization(verification_keys, capsule, kfrags):
verifying_pk=verifying_pk, verifying_pk=verifying_pk,
delegating_pk=delegating_pk, delegating_pk=delegating_pk,
receiving_pk=delegating_pk, receiving_pk=delegating_pk,
metadata=metadata,
) )
# Wrong signing key # Wrong signing key
@ -64,34 +43,6 @@ def test_cfrag_serialization(verification_keys, capsule, kfrags):
verifying_pk=receiving_pk, verifying_pk=receiving_pk,
delegating_pk=delegating_pk, delegating_pk=delegating_pk,
receiving_pk=receiving_pk, receiving_pk=receiving_pk,
metadata=metadata,
)
def test_cfrag_serialization_no_metadata(verification_keys, capsule, kfrags):
verifying_pk, delegating_pk, receiving_pk = verification_keys
for kfrag in kfrags:
# Create with no metadata
cfrag = reencrypt(capsule, kfrag)
cfrag_bytes = bytes(cfrag)
new_cfrag = CapsuleFrag.from_bytes(cfrag_bytes)
verified_cfrag = new_cfrag.verify(capsule,
verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
)
assert verified_cfrag == cfrag
with pytest.raises(VerificationError):
new_cfrag.verify(capsule,
verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
metadata=b'some metadata',
) )
@ -103,8 +54,7 @@ def test_cfrag_with_wrong_capsule(verification_keys, kfrags, capsule_and_ciphert
capsule_alice1 = capsule capsule_alice1 = capsule
capsule_alice2, _unused_key2 = Capsule.from_public_key(delegating_pk) capsule_alice2, _unused_key2 = Capsule.from_public_key(delegating_pk)
metadata = b"some metadata" cfrag = reencrypt(capsule_alice2, kfrags[0])
cfrag = reencrypt(capsule_alice2, kfrags[0], metadata=metadata)
cfrag = CapsuleFrag.from_bytes(bytes(cfrag)) # de-verify cfrag = CapsuleFrag.from_bytes(bytes(cfrag)) # de-verify
with pytest.raises(VerificationError): with pytest.raises(VerificationError):
@ -112,7 +62,6 @@ def test_cfrag_with_wrong_capsule(verification_keys, kfrags, capsule_and_ciphert
verifying_pk=verifying_pk, verifying_pk=verifying_pk,
delegating_pk=delegating_pk, delegating_pk=delegating_pk,
receiving_pk=receiving_pk, receiving_pk=receiving_pk,
metadata=metadata,
) )
@ -121,8 +70,7 @@ def test_cfrag_with_wrong_data(verification_keys, kfrags, capsule_and_ciphertext
capsule, ciphertext = capsule_and_ciphertext capsule, ciphertext = capsule_and_ciphertext
verifying_pk, delegating_pk, receiving_pk = verification_keys verifying_pk, delegating_pk, receiving_pk = verification_keys
metadata = b"some metadata" cfrag = reencrypt(capsule, kfrags[0])
cfrag = reencrypt(capsule, kfrags[0], metadata=metadata)
# Let's put random garbage in one of the cfrags # Let's put random garbage in one of the cfrags
cfrag = CapsuleFrag.from_bytes(bytes(cfrag)) # de-verify cfrag = CapsuleFrag.from_bytes(bytes(cfrag)) # de-verify
@ -134,7 +82,6 @@ def test_cfrag_with_wrong_data(verification_keys, kfrags, capsule_and_ciphertext
verifying_pk=verifying_pk, verifying_pk=verifying_pk,
delegating_pk=delegating_pk, delegating_pk=delegating_pk,
receiving_pk=receiving_pk, receiving_pk=receiving_pk,
metadata=metadata,
) )
@ -142,8 +89,8 @@ def test_cfrag_is_hashable(verification_keys, capsule, kfrags):
verifying_pk, delegating_pk, receiving_pk = verification_keys verifying_pk, delegating_pk, receiving_pk = verification_keys
cfrag0 = reencrypt(capsule, kfrags[0], metadata=b'abcdef') cfrag0 = reencrypt(capsule, kfrags[0])
cfrag1 = reencrypt(capsule, kfrags[1], metadata=b'abcdef') cfrag1 = reencrypt(capsule, kfrags[1])
assert hash(cfrag0) != hash(cfrag1) assert hash(cfrag0) != hash(cfrag1)
@ -154,16 +101,22 @@ def test_cfrag_is_hashable(verification_keys, capsule, kfrags):
verifying_pk=verifying_pk, verifying_pk=verifying_pk,
delegating_pk=delegating_pk, delegating_pk=delegating_pk,
receiving_pk=receiving_pk, receiving_pk=receiving_pk,
metadata=b'abcdef') )
assert hash(verified_cfrag) == hash(cfrag0) assert hash(verified_cfrag) == hash(cfrag0)
def test_cfrag_str(capsule, kfrags): def test_cfrag_str(capsule, kfrags):
cfrag0 = reencrypt(capsule, kfrags[0], metadata=b'abcdef') cfrag0 = reencrypt(capsule, kfrags[0])
s = str(cfrag0) s = str(cfrag0)
assert 'VerifiedCapsuleFrag' in s assert 'VerifiedCapsuleFrag' in s
s = str(CapsuleFrag.from_bytes(bytes(cfrag0))) s = str(CapsuleFrag.from_bytes(bytes(cfrag0)))
assert "VerifiedCapsuleFrag" not in s assert "VerifiedCapsuleFrag" not in s
assert "CapsuleFrag" in s assert "CapsuleFrag" in s
def test_serialized_size(capsule, kfrags):
verified_cfrag = reencrypt(capsule, kfrags[0])
cfrag = CapsuleFrag.from_bytes(bytes(verified_cfrag))
assert verified_cfrag.serialized_size() == cfrag.serialized_size()

View File

@ -143,16 +143,16 @@ def test_kfrags(implementations):
def _reencrypt(umbral, verifying_pk_bytes, delegating_pk_bytes, receiving_pk_bytes, def _reencrypt(umbral, verifying_pk_bytes, delegating_pk_bytes, receiving_pk_bytes,
capsule_bytes, kfrags_bytes, threshold, metadata): capsule_bytes, kfrags_bytes, threshold):
capsule = umbral.Capsule.from_bytes(bytes(capsule_bytes)) capsule = umbral.Capsule.from_bytes(bytes(capsule_bytes))
verified_kfrags = _verify_kfrags(umbral, kfrags_bytes, verified_kfrags = _verify_kfrags(umbral, kfrags_bytes,
verifying_pk_bytes, delegating_pk_bytes, receiving_pk_bytes) verifying_pk_bytes, delegating_pk_bytes, receiving_pk_bytes)
cfrags = [umbral.reencrypt(capsule, kfrag, metadata=metadata) for kfrag in verified_kfrags[:threshold]] cfrags = [umbral.reencrypt(capsule, kfrag) for kfrag in verified_kfrags[:threshold]]
return [bytes(cfrag) for cfrag in cfrags] return [bytes(cfrag) for cfrag in cfrags]
def _decrypt_reencrypted(umbral, receiving_sk_bytes, delegating_pk_bytes, verifying_pk_bytes, def _decrypt_reencrypted(umbral, receiving_sk_bytes, delegating_pk_bytes, verifying_pk_bytes,
capsule_bytes, cfrags_bytes, ciphertext, metadata): capsule_bytes, cfrags_bytes, ciphertext):
receiving_sk = umbral.SecretKey.from_bytes(receiving_sk_bytes) receiving_sk = umbral.SecretKey.from_bytes(receiving_sk_bytes)
receiving_pk = umbral.PublicKey.from_secret_key(receiving_sk) receiving_pk = umbral.PublicKey.from_secret_key(receiving_sk)
@ -166,11 +166,11 @@ def _decrypt_reencrypted(umbral, receiving_sk_bytes, delegating_pk_bytes, verify
verifying_pk=verifying_pk, verifying_pk=verifying_pk,
delegating_pk=delegating_pk, delegating_pk=delegating_pk,
receiving_pk=receiving_pk, receiving_pk=receiving_pk,
metadata=metadata) )
for cfrag in cfrags] for cfrag in cfrags]
# Decryption by Bob # Decryption by Bob
plaintext = umbral.decrypt_reencrypted(decrypting_sk=receiving_sk, plaintext = umbral.decrypt_reencrypted(receiving_sk=receiving_sk,
delegating_pk=delegating_pk, delegating_pk=delegating_pk,
capsule=capsule, capsule=capsule,
verified_cfrags=verified_cfrags, verified_cfrags=verified_cfrags,
@ -184,7 +184,6 @@ def test_reencrypt(implementations):
umbral1, umbral2 = implementations umbral1, umbral2 = implementations
metadata = b'metadata'
threshold = 2 threshold = 2
num_kfrags = 3 num_kfrags = 3
plaintext = b'peace at dawn' plaintext = b'peace at dawn'
@ -203,13 +202,13 @@ def test_reencrypt(implementations):
# On client 2 # On client 2
cfrags_bytes = _reencrypt(umbral2, verifying_pk_bytes, delegating_pk_bytes, receiving_pk_bytes, cfrags_bytes = _reencrypt(umbral2, verifying_pk_bytes, delegating_pk_bytes, receiving_pk_bytes,
capsule_bytes, kfrags_bytes, threshold, metadata) capsule_bytes, kfrags_bytes, threshold)
# On client 1 # On client 1
plaintext_reencrypted = _decrypt_reencrypted(umbral1, plaintext_reencrypted = _decrypt_reencrypted(umbral1,
receiving_sk_bytes, delegating_pk_bytes, verifying_pk_bytes, receiving_sk_bytes, delegating_pk_bytes, verifying_pk_bytes,
capsule_bytes, cfrags_bytes, ciphertext, metadata) capsule_bytes, cfrags_bytes, ciphertext)
assert plaintext_reencrypted == plaintext assert plaintext_reencrypted == plaintext
@ -243,3 +242,30 @@ def test_signer(implementations):
assert _verify_message(umbral1, pk_bytes, signature2_bytes, message) assert _verify_message(umbral1, pk_bytes, signature2_bytes, message)
assert _verify_message(umbral2, pk_bytes, signature1_bytes, message) assert _verify_message(umbral2, pk_bytes, signature1_bytes, message)
def _measure_sizes(umbral):
sized_types = [
umbral.SecretKey,
umbral.SecretKeyFactory,
umbral.PublicKey,
umbral.Signature,
umbral.Capsule,
umbral.KeyFrag,
umbral.VerifiedKeyFrag,
umbral.CapsuleFrag,
umbral.VerifiedCapsuleFrag,
]
return {tp.__name__: tp.serialized_size() for tp in sized_types}
def test_serialization_size(implementations):
umbral1, umbral2 = implementations
sizes1 = _measure_sizes(umbral1)
sizes2 = _measure_sizes(umbral1)
assert sizes1 == sizes2

View File

@ -1,7 +1,6 @@
import pytest import pytest
import os import os
from umbral import GenericError
from umbral.dem import DEM from umbral.dem import DEM
@ -48,7 +47,7 @@ def test_malformed_ciphertext():
dem.decrypt(ciphertext[:DEM.NONCE_SIZE + DEM.TAG_SIZE - 1]) dem.decrypt(ciphertext[:DEM.NONCE_SIZE + DEM.TAG_SIZE - 1])
# Too long # Too long
with pytest.raises(GenericError): with pytest.raises(ValueError):
dem.decrypt(ciphertext + b'abcd') dem.decrypt(ciphertext + b'abcd')
@ -77,5 +76,5 @@ def test_encrypt_decrypt_associated_data():
assert cleartext1 == plaintext assert cleartext1 == plaintext
# Attempt decryption with invalid associated data # Attempt decryption with invalid associated data
with pytest.raises(GenericError): with pytest.raises(ValueError):
cleartext2 = dem.decrypt(ciphertext0, authenticated_data=b'wrong data') cleartext2 = dem.decrypt(ciphertext0, authenticated_data=b'wrong data')

View File

@ -1,7 +1,7 @@
import pytest import pytest
from umbral import KeyFrag, PublicKey, Signer, VerificationError from umbral import KeyFrag, PublicKey, Signer, VerificationError
from umbral.key_frag import KeyFragID, KeyFragBase from umbral.key_frag import KeyFragID, KeyFragBase, VerifiedKeyFrag
from umbral.curve_scalar import CurveScalar from umbral.curve_scalar import CurveScalar
@ -124,3 +124,15 @@ def test_kfrag_str(kfrags):
s = str(KeyFrag.from_bytes(bytes(kfrags[0]))) s = str(KeyFrag.from_bytes(bytes(kfrags[0])))
assert "VerifiedKeyFrag" not in s assert "VerifiedKeyFrag" not in s
assert "KeyFrag" in s assert "KeyFrag" in s
def test_from_verified_bytes(kfrags):
kfrag_bytes = bytes(kfrags[0])
verified_kfrag = VerifiedKeyFrag.from_verified_bytes(kfrag_bytes)
assert verified_kfrag == kfrags[0]
def test_serialized_size(kfrags):
verified_kfrag = kfrags[0]
kfrag = KeyFrag.from_bytes(bytes(verified_kfrag))
assert verified_kfrag.serialized_size() == kfrag.serialized_size()

View File

@ -4,7 +4,6 @@ from umbral import (
SecretKey, SecretKey,
PublicKey, PublicKey,
Signer, Signer,
GenericError,
KeyFrag, KeyFrag,
CapsuleFrag, CapsuleFrag,
encrypt, encrypt,
@ -25,7 +24,7 @@ def test_public_key_encryption(alices_keys):
# Wrong secret key # Wrong secret key
sk = SecretKey.random() sk = SecretKey.random()
with pytest.raises(GenericError): with pytest.raises(ValueError):
decrypt_original(sk, capsule, ciphertext) decrypt_original(sk, capsule, ciphertext)
@ -81,7 +80,7 @@ def test_simple_api(num_kfrags, threshold):
cfrags = [reencrypt(capsule, kfrag) for kfrag in kfrags] cfrags = [reencrypt(capsule, kfrag) for kfrag in kfrags]
# Decryption by Bob # Decryption by Bob
plaintext_reenc = decrypt_reencrypted(decrypting_sk=receiving_sk, plaintext_reenc = decrypt_reencrypted(receiving_sk=receiving_sk,
delegating_pk=delegating_pk, delegating_pk=delegating_pk,
capsule=capsule, capsule=capsule,
verified_cfrags=cfrags[:threshold], verified_cfrags=cfrags[:threshold],
@ -105,7 +104,7 @@ def test_decrypt_unverified_cfrag(verification_keys, bobs_keys, capsule_and_ciph
cfrags = [reencrypt(capsule, kfrag) for kfrag in kfrags] cfrags = [reencrypt(capsule, kfrag) for kfrag in kfrags]
cfrags[0] = CapsuleFrag.from_bytes(bytes(cfrags[0])) cfrags[0] = CapsuleFrag.from_bytes(bytes(cfrags[0]))
with pytest.raises(TypeError): with pytest.raises(TypeError):
plaintext_reenc = decrypt_reencrypted(decrypting_sk=receiving_sk, plaintext_reenc = decrypt_reencrypted(receiving_sk=receiving_sk,
delegating_pk=delegating_pk, delegating_pk=delegating_pk,
capsule=capsule, capsule=capsule,
verified_cfrags=cfrags, verified_cfrags=cfrags,

View File

@ -2,55 +2,65 @@ import re
import pytest import pytest
from umbral.serializable import Serializable, serialize_bool, take_bool from umbral.serializable import Serializable, Deserializable, bool_bytes, bool_from_exact_bytes
class A(Serializable): class A(Serializable, Deserializable):
def __init__(self, val: int): def __init__(self, val: int):
assert 0 <= val < 2**32 assert 0 <= val < 2**32
self.val = val self.val = val
@classmethod @classmethod
def __take__(cls, data): def serialized_size(cls):
val_bytes, data = cls.__take_bytes__(data, 4) return 4
return cls(int.from_bytes(val_bytes, byteorder='big')), data
@classmethod
def _from_exact_bytes(cls, data):
return cls(int.from_bytes(data, byteorder='big'))
def __bytes__(self): def __bytes__(self):
return self.val.to_bytes(4, byteorder='big') return self.val.to_bytes(self.serialized_size(), byteorder='big')
def __eq__(self, other): def __eq__(self, other):
return isinstance(other, A) and self.val == other.val return isinstance(other, A) and self.val == other.val
class B(Serializable): class B(Serializable, Deserializable):
def __init__(self, val: int): def __init__(self, val: int):
assert 0 <= val < 2**16 assert 0 <= val < 2**16
self.val = val self.val = val
@classmethod @classmethod
def __take__(cls, data): def serialized_size(cls):
val_bytes, data = cls.__take_bytes__(data, 2) return 2
return cls(int.from_bytes(val_bytes, byteorder='big')), data
@classmethod
def _from_exact_bytes(cls, data):
return cls(int.from_bytes(data, byteorder='big'))
def __bytes__(self): def __bytes__(self):
return self.val.to_bytes(2, byteorder='big') return self.val.to_bytes(self.serialized_size(), byteorder='big')
def __eq__(self, other): def __eq__(self, other):
return isinstance(other, B) and self.val == other.val return isinstance(other, B) and self.val == other.val
class C(Serializable): class C(Serializable, Deserializable):
def __init__(self, a: A, b: B): def __init__(self, a: A, b: B):
self.a = a self.a = a
self.b = b self.b = b
@classmethod @classmethod
def __take__(cls, data): def serialized_size(cls):
components, data = cls.__take_types__(data, A, B) return A.serialized_size() + B.serialized_size()
return cls(*components), data
@classmethod
def _from_exact_bytes(cls, data):
components = cls._split(data, A, B)
return cls(*components)
def __bytes__(self): def __bytes__(self):
return bytes(self.a) + bytes(self.b) return bytes(self.a) + bytes(self.b)
@ -71,7 +81,7 @@ def test_too_many_bytes():
a = A(2**32 - 123) a = A(2**32 - 123)
b = B(2**16 - 456) b = B(2**16 - 456)
c = C(a, b) c = C(a, b)
with pytest.raises(ValueError, match="1 bytes remaining after deserializing"): with pytest.raises(ValueError, match="Expected 6 bytes, got 7"):
C.from_bytes(bytes(c) + b'\x00') C.from_bytes(bytes(c) + b'\x00')
@ -80,13 +90,22 @@ def test_not_enough_bytes():
b = B(2**16 - 456) b = B(2**16 - 456)
c = C(a, b) c = C(a, b)
# Will happen on deserialization of B - 1 byte missing # Will happen on deserialization of B - 1 byte missing
with pytest.raises(ValueError, match="cannot take 2 bytes from a bytestring of size 1"): with pytest.raises(ValueError, match="Expected 6 bytes, got 5"):
C.from_bytes(bytes(c)[:-1]) C.from_bytes(bytes(c)[:-1])
def test_serialize_bool(): def test_bool_bytes():
assert take_bool(serialize_bool(True) + b'1234') == (True, b'1234') assert bool_from_exact_bytes(bool_bytes(True)) == True
assert take_bool(serialize_bool(False) + b'12') == (False, b'12') assert bool_from_exact_bytes(bool_bytes(False)) == False
error_msg = re.escape("Incorrectly serialized boolean; expected b'\\x00' or b'\\x01', got b'z'") error_msg = re.escape("Incorrectly serialized boolean; expected b'\\x00' or b'\\x01', got b'z'")
with pytest.raises(ValueError, match=error_msg): with pytest.raises(ValueError, match=error_msg):
take_bool(b'z1234') bool_from_exact_bytes(b'z')
def test_split_bool():
a = A(2**32 - 123)
b = True
data = bytes(a) + bool_bytes(b)
a_back, b_back = Deserializable._split(data, A, bool)
assert a_back == a
assert b_back == b

View File

@ -147,14 +147,12 @@ def test_cfrags():
CapsuleFrag.from_bytes(bytes.fromhex(json_kfrag['cfrag']))) CapsuleFrag.from_bytes(bytes.fromhex(json_kfrag['cfrag'])))
for json_kfrag in vector_suite['vectors']] for json_kfrag in vector_suite['vectors']]
metadata = bytes.fromhex(vector_suite['metadata'])
for kfrag, cfrag in kfrags_n_cfrags: for kfrag, cfrag in kfrags_n_cfrags:
verified_kfrag = kfrag.verify(verifying_pk=verifying_pk, verified_kfrag = kfrag.verify(verifying_pk=verifying_pk,
delegating_pk=delegating_pk, delegating_pk=delegating_pk,
receiving_pk=receiving_pk) receiving_pk=receiving_pk)
new_cfrag = reencrypt(capsule, verified_kfrag, metadata=metadata).cfrag new_cfrag = reencrypt(capsule, verified_kfrag).cfrag
assert new_cfrag.point_e1 == cfrag.point_e1 assert new_cfrag.point_e1 == cfrag.point_e1
assert new_cfrag.point_v1 == cfrag.point_v1 assert new_cfrag.point_v1 == cfrag.point_v1
assert new_cfrag.kfrag_id == cfrag.kfrag_id assert new_cfrag.kfrag_id == cfrag.kfrag_id
@ -163,4 +161,4 @@ def test_cfrags():
verifying_pk=verifying_pk, verifying_pk=verifying_pk,
delegating_pk=delegating_pk, delegating_pk=delegating_pk,
receiving_pk=receiving_pk, receiving_pk=receiving_pk,
metadata=metadata) )

View File

@ -4,7 +4,7 @@ from .__about__ import (
from .capsule import Capsule from .capsule import Capsule
from .capsule_frag import CapsuleFrag, VerifiedCapsuleFrag from .capsule_frag import CapsuleFrag, VerifiedCapsuleFrag
from .errors import GenericError, VerificationError from .errors import VerificationError
from .key_frag import KeyFrag, VerifiedKeyFrag from .key_frag import KeyFrag, VerifiedKeyFrag
from .keys import SecretKey, PublicKey, SecretKeyFactory from .keys import SecretKey, PublicKey, SecretKeyFactory
from .pre import encrypt, decrypt_original, decrypt_reencrypted, reencrypt, generate_kfrags from .pre import encrypt, decrypt_original, decrypt_reencrypted, reencrypt, generate_kfrags
@ -21,7 +21,6 @@ __all__ = [
"VerifiedKeyFrag", "VerifiedKeyFrag",
"CapsuleFrag", "CapsuleFrag",
"VerifiedCapsuleFrag", "VerifiedCapsuleFrag",
"GenericError",
"VerificationError", "VerificationError",
"encrypt", "encrypt",
"decrypt_original", "decrypt_original",

View File

@ -2,10 +2,9 @@ from typing import TYPE_CHECKING, Tuple, Sequence
from .curve_point import CurvePoint from .curve_point import CurvePoint
from .curve_scalar import CurveScalar from .curve_scalar import CurveScalar
from .errors import GenericError
from .hashing import hash_capsule_points, hash_to_polynomial_arg, hash_to_shared_secret from .hashing import hash_capsule_points, hash_to_polynomial_arg, hash_to_shared_secret
from .keys import PublicKey, SecretKey from .keys import PublicKey, SecretKey
from .serializable import Serializable from .serializable import Serializable, Deserializable
if TYPE_CHECKING: # pragma: no cover if TYPE_CHECKING: # pragma: no cover
from .capsule_frag import CapsuleFrag from .capsule_frag import CapsuleFrag
@ -19,7 +18,7 @@ def lambda_coeff(xs: Sequence[CurveScalar], i: int) -> CurveScalar:
return res return res
class Capsule(Serializable): class Capsule(Serializable, Deserializable):
""" """
Encapsulated symmetric key. Encapsulated symmetric key.
""" """
@ -29,21 +28,25 @@ class Capsule(Serializable):
self.point_v = point_v self.point_v = point_v
self.signature = signature self.signature = signature
_COMPONENT_TYPES = CurvePoint, CurvePoint, CurveScalar
_SERIALIZED_SIZE = sum(tp.serialized_size() for tp in _COMPONENT_TYPES)
@classmethod @classmethod
def __take__(cls, data: bytes) -> Tuple['Capsule', bytes]: def serialized_size(cls):
(e, v, sig), data = cls.__take_types__(data, CurvePoint, CurvePoint, CurveScalar) return cls._SERIALIZED_SIZE
capsule = cls(e, v, sig) @classmethod
def _from_exact_bytes(cls, data: bytes):
capsule = cls(*cls._split(data, *cls._COMPONENT_TYPES))
if not capsule._verify(): if not capsule._verify():
raise GenericError("Capsule self-verification failed. Serialized data may be damaged.") raise ValueError("Capsule self-verification failed. Serialized data may be damaged.")
return capsule
return capsule, data def __bytes__(self):
def __bytes__(self) -> bytes:
return bytes(self.point_e) + bytes(self.point_v) + bytes(self.signature) return bytes(self.point_e) + bytes(self.point_v) + bytes(self.signature)
@classmethod @classmethod
def from_public_key(cls, pk: PublicKey) -> Tuple['Capsule', CurvePoint]: def from_public_key(cls, delegating_pk: PublicKey) -> Tuple['Capsule', CurvePoint]:
g = CurvePoint.generator() g = CurvePoint.generator()
priv_r = CurveScalar.random_nonzero() priv_r = CurveScalar.random_nonzero()
@ -55,12 +58,12 @@ class Capsule(Serializable):
h = hash_capsule_points(pub_r, pub_u) h = hash_capsule_points(pub_r, pub_u)
s = priv_u + (priv_r * h) s = priv_u + (priv_r * h)
shared_key = pk._point_key * (priv_r + priv_u) shared_key = delegating_pk._point_key * (priv_r + priv_u)
return cls(point_e=pub_r, point_v=pub_u, signature=s), shared_key return cls(point_e=pub_r, point_v=pub_u, signature=s), shared_key
def open_original(self, sk: SecretKey) -> CurvePoint: def open_original(self, delegating_sk: SecretKey) -> CurvePoint:
return (self.point_e + self.point_v) * sk.secret_scalar() return (self.point_e + self.point_v) * delegating_sk.secret_scalar()
def open_reencrypted(self, def open_reencrypted(self,
receiving_sk: SecretKey, receiving_sk: SecretKey,
@ -106,7 +109,7 @@ class Capsule(Serializable):
# TODO: check for d == 0? Or just let if fail? # TODO: check for d == 0? Or just let if fail?
inv_d = d.invert() inv_d = d.invert()
if orig_pub_key * (s * inv_d) != (e_prime * h) + v_prime: if orig_pub_key * (s * inv_d) != (e_prime * h) + v_prime:
raise GenericError("Internal validation failed") raise ValueError("Internal validation failed")
return (e_prime + v_prime) * d return (e_prime + v_prime) * d

View File

@ -1,4 +1,4 @@
from typing import Optional, Tuple from typing import Optional, Tuple, Type
from .capsule import Capsule from .capsule import Capsule
from .curve_point import CurvePoint from .curve_point import CurvePoint
@ -8,11 +8,11 @@ from .hashing import hash_to_cfrag_verification, kfrag_signature_message
from .keys import PublicKey from .keys import PublicKey
from .key_frag import KeyFrag, KeyFragID from .key_frag import KeyFrag, KeyFragID
from .params import PARAMETERS from .params import PARAMETERS
from .serializable import Serializable from .serializable import Serializable, Deserializable, HasSerializedSize
from .signing import Signature from .signing import Signature
class CapsuleFragProof(Serializable): class CapsuleFragProof(Serializable, Deserializable):
def __init__(self, def __init__(self,
point_e2: CurvePoint, point_e2: CurvePoint,
@ -34,23 +34,23 @@ class CapsuleFragProof(Serializable):
return (self.point_e2, self.point_v2, self.kfrag_commitment, return (self.point_e2, self.point_v2, self.kfrag_commitment,
self.kfrag_pok, self.signature, self.kfrag_signature) self.kfrag_pok, self.signature, self.kfrag_signature)
_COMPONENT_TYPES: Tuple[Type[HasSerializedSize], ...] = (
CurvePoint, CurvePoint, CurvePoint, CurvePoint, CurveScalar, Signature)
_SERIALIZED_SIZE = sum(tp.serialized_size() for tp in _COMPONENT_TYPES)
def __eq__(self, other): def __eq__(self, other):
return self._components() == other._components() return self._components() == other._components()
@classmethod @classmethod
def __take__(cls, data): def serialized_size(cls):
types = [CurvePoint, CurvePoint, CurvePoint, CurvePoint, CurveScalar, Signature] return cls._SERIALIZED_SIZE
components, data = cls.__take_types__(data, *types)
return cls(*components), data @classmethod
def _from_exact_bytes(cls, data):
return cls(*cls._split(data, *cls._COMPONENT_TYPES))
def __bytes__(self): def __bytes__(self):
return (bytes(self.point_e2) + return b''.join(bytes(comp) for comp in self._components())
bytes(self.point_v2) +
bytes(self.kfrag_commitment) +
bytes(self.kfrag_pok) +
bytes(self.signature) +
bytes(self.kfrag_signature)
)
@classmethod @classmethod
def from_kfrag_and_cfrag(cls, def from_kfrag_and_cfrag(cls,
@ -58,7 +58,6 @@ class CapsuleFragProof(Serializable):
kfrag: KeyFrag, kfrag: KeyFrag,
cfrag_e1: CurvePoint, cfrag_e1: CurvePoint,
cfrag_v1: CurvePoint, cfrag_v1: CurvePoint,
metadata: Optional[bytes],
) -> 'CapsuleFragProof': ) -> 'CapsuleFragProof':
params = PARAMETERS params = PARAMETERS
@ -81,7 +80,7 @@ class CapsuleFragProof(Serializable):
v2 = v * t v2 = v * t
u2 = u * t u2 = u * t
h = hash_to_cfrag_verification([e, e1, e2, v, v1, v2, u, u1, u2], metadata) h = hash_to_cfrag_verification([e, e1, e2, v, v1, v2, u, u1, u2])
### ###
@ -96,7 +95,7 @@ class CapsuleFragProof(Serializable):
) )
class CapsuleFrag(Serializable): class CapsuleFrag(Serializable, Deserializable):
""" """
Re-encrypted fragment of :py:class:`Capsule`. Re-encrypted fragment of :py:class:`Capsule`.
""" """
@ -118,6 +117,10 @@ class CapsuleFrag(Serializable):
def _components(self): def _components(self):
return (self.point_e1, self.point_v1, self.kfrag_id, self.precursor, self.proof) return (self.point_e1, self.point_v1, self.kfrag_id, self.precursor, self.proof)
_COMPONENT_TYPES: Tuple[Type[HasSerializedSize], ...] = (
CurvePoint, CurvePoint, KeyFragID, CurvePoint, CapsuleFragProof)
_SERIALIZED_SIZE = sum(tp.serialized_size() for tp in _COMPONENT_TYPES)
def __eq__(self, other): def __eq__(self, other):
return self._components() == other._components() return self._components() == other._components()
@ -128,28 +131,22 @@ class CapsuleFrag(Serializable):
return f"{self.__class__.__name__}:{bytes(self).hex()[:16]}" return f"{self.__class__.__name__}:{bytes(self).hex()[:16]}"
@classmethod @classmethod
def __take__(cls, data): def serialized_size(cls):
types = CurvePoint, CurvePoint, KeyFragID, CurvePoint, CapsuleFragProof return cls._SERIALIZED_SIZE
components, data = cls.__take_types__(data, *types)
return cls(*components), data
def __bytes__(self):
return (bytes(self.point_e1) +
bytes(self.point_v1) +
bytes(self.kfrag_id) +
bytes(self.precursor) +
bytes(self.proof))
@classmethod @classmethod
def reencrypted(cls, def _from_exact_bytes(cls, data):
capsule: Capsule, return cls(*cls._split(data, *cls._COMPONENT_TYPES))
kfrag: KeyFrag,
metadata: Optional[bytes] = None, def __bytes__(self):
) -> 'CapsuleFrag': return b''.join(bytes(comp) for comp in self._components())
@classmethod
def reencrypted(cls, capsule: Capsule, kfrag: KeyFrag) -> 'CapsuleFrag':
rk = kfrag.key rk = kfrag.key
e1 = capsule.point_e * rk e1 = capsule.point_e * rk
v1 = capsule.point_v * rk v1 = capsule.point_v * rk
proof = CapsuleFragProof.from_kfrag_and_cfrag(capsule, kfrag, e1, v1, metadata) proof = CapsuleFragProof.from_kfrag_and_cfrag(capsule, kfrag, e1, v1)
return cls(point_e1=e1, return cls(point_e1=e1,
point_v1=v1, point_v1=v1,
@ -163,12 +160,9 @@ class CapsuleFrag(Serializable):
verifying_pk: PublicKey, verifying_pk: PublicKey,
delegating_pk: PublicKey, delegating_pk: PublicKey,
receiving_pk: PublicKey, receiving_pk: PublicKey,
metadata: Optional[bytes] = None,
) -> 'VerifiedCapsuleFrag': ) -> 'VerifiedCapsuleFrag':
""" """
Verifies the validity of this fragment. Verifies the validity of this fragment.
``metadata`` should coincide with the one given to :py:func:`reencrypt`.
""" """
params = PARAMETERS params = PARAMETERS
@ -189,7 +183,7 @@ class CapsuleFrag(Serializable):
v2 = self.proof.point_v2 v2 = self.proof.point_v2
u2 = self.proof.kfrag_pok u2 = self.proof.kfrag_pok
h = hash_to_cfrag_verification([e, e1, e2, v, v1, v2, u, u1, u2], metadata) h = hash_to_cfrag_verification([e, e1, e2, v, v1, v2, u, u1, u2])
### ###
@ -221,7 +215,7 @@ class CapsuleFrag(Serializable):
return VerifiedCapsuleFrag(self) return VerifiedCapsuleFrag(self)
class VerifiedCapsuleFrag: class VerifiedCapsuleFrag(Serializable):
""" """
Verified capsule frag, good for decryption. Verified capsule frag, good for decryption.
Can be cast to ``bytes``, but cannot be deserialized from bytes directly. Can be cast to ``bytes``, but cannot be deserialized from bytes directly.
@ -234,6 +228,10 @@ class VerifiedCapsuleFrag:
def __bytes__(self): def __bytes__(self):
return bytes(self.cfrag) return bytes(self.cfrag)
@classmethod
def serialized_size(cls):
return CapsuleFrag.serialized_size()
def __eq__(self, other): def __eq__(self, other):
return self.cfrag == other.cfrag return self.cfrag == other.cfrag

View File

@ -3,10 +3,10 @@ from typing import Tuple
from . import openssl from . import openssl
from .curve import CURVE from .curve import CURVE
from .curve_scalar import CurveScalar from .curve_scalar import CurveScalar
from .serializable import Serializable from .serializable import Serializable, Deserializable
class CurvePoint(Serializable): class CurvePoint(Serializable, Deserializable):
""" """
Represents an OpenSSL EC_POINT except more Pythonic. Represents an OpenSSL EC_POINT except more Pythonic.
""" """
@ -34,14 +34,15 @@ class CurvePoint(Serializable):
return openssl.point_to_affine_coords(CURVE, self._backend_point) return openssl.point_to_affine_coords(CURVE, self._backend_point)
@classmethod @classmethod
def __take__(cls, data: bytes) -> Tuple['CurvePoint', bytes]: def serialized_size(cls):
return CURVE.field_element_size + 1 # compressed point size
@classmethod
def _from_exact_bytes(cls, data: bytes):
""" """
Returns a CurvePoint object from the given byte data on the curve provided. Returns a CurvePoint object from the given byte data on the curve provided.
""" """
size = CURVE.field_element_size + 1 # compressed point size return cls(openssl.point_from_bytes(CURVE, data))
point_data, data = cls.__take_bytes__(data, size)
point = openssl.point_from_bytes(CURVE, point_data)
return cls(point), data
def __bytes__(self) -> bytes: def __bytes__(self) -> bytes:
""" """

View File

@ -2,12 +2,12 @@ from typing import TYPE_CHECKING, Union, Tuple
from . import openssl from . import openssl
from .curve import CURVE from .curve import CURVE
from .serializable import Serializable from .serializable import Serializable, Deserializable
if TYPE_CHECKING: # pragma: no cover if TYPE_CHECKING: # pragma: no cover
from .hashing import Hash from .hashing import Hash
class CurveScalar(Serializable): class CurveScalar(Serializable, Deserializable):
""" """
Represents an OpenSSL Bignum modulo the order of a curve. Some of these Represents an OpenSSL Bignum modulo the order of a curve. Some of these
operations will only work with prime numbers. operations will only work with prime numbers.
@ -45,10 +45,12 @@ class CurveScalar(Serializable):
return cls(openssl.bn_from_bytes(digest.finalize(), apply_modulus=CURVE.bn_order)) return cls(openssl.bn_from_bytes(digest.finalize(), apply_modulus=CURVE.bn_order))
@classmethod @classmethod
def __take__(cls, data: bytes) -> Tuple['CurveScalar', bytes]: def serialized_size(cls):
scalar_data, data = cls.__take_bytes__(data, CURVE.scalar_size) return CURVE.scalar_size
bignum = openssl.bn_from_bytes(scalar_data, check_modulus=CURVE.bn_order)
return cls(bignum), data @classmethod
def _from_exact_bytes(cls, data: bytes):
return cls(openssl.bn_from_bytes(data, check_modulus=CURVE.bn_order))
def __bytes__(self) -> bytes: def __bytes__(self) -> bytes:
""" """

View File

@ -14,7 +14,6 @@ from nacl.bindings.crypto_aead import (
) )
from . import openssl from . import openssl
from .errors import GenericError
def kdf(data: bytes, def kdf(data: bytes,
@ -64,6 +63,6 @@ class DEM:
try: try:
return xchacha_decrypt(ciphertext, authenticated_data, nonce, self._key) return xchacha_decrypt(ciphertext, authenticated_data, nonce, self._key)
except nacl.exceptions.CryptoError as e: except nacl.exceptions.CryptoError as e:
raise GenericError("Decryption of ciphertext failed: " raise ValueError("Decryption of ciphertext failed: "
"either someone tampered with the ciphertext or " "either someone tampered with the ciphertext or "
"you are using an incorrect decryption key.") from e "you are using an incorrect decryption key.") from e

View File

@ -1,10 +1,4 @@
class GenericError(Exception): class VerificationError(Exception):
"""
An interal Umbral error, see the message for details.
"""
class VerificationError(GenericError):
""" """
Integrity of the data cannot be verified, see the message for details. Integrity of the data cannot be verified, see the message for details.
""" """

View File

@ -6,7 +6,7 @@ from .openssl import backend, ErrorInvalidCompressedPoint
from .curve import CURVE from .curve import CURVE
from .curve_scalar import CurveScalar from .curve_scalar import CurveScalar
from .curve_point import CurvePoint from .curve_point import CurvePoint
from .serializable import Serializable, serialize_bool from .serializable import Serializable, bool_bytes
if TYPE_CHECKING: # pragma: no cover if TYPE_CHECKING: # pragma: no cover
from .key_frag import KeyFragID from .key_frag import KeyFragID
@ -63,14 +63,10 @@ def hash_to_shared_secret(precursor: CurvePoint,
return CurveScalar.from_digest(digest) return CurveScalar.from_digest(digest)
def hash_to_cfrag_verification(points: Iterable[CurvePoint], def hash_to_cfrag_verification(points: Iterable[CurvePoint]) -> CurveScalar:
metadata: Optional[bytes] = None
) -> CurveScalar:
digest = Hash(b"CFRAG_VERIFICATION") digest = Hash(b"CFRAG_VERIFICATION")
for point in points: for point in points:
digest.update(point) digest.update(point)
if metadata is not None:
digest.update(metadata)
return CurveScalar.from_digest(digest) return CurveScalar.from_digest(digest)
@ -83,14 +79,14 @@ def kfrag_signature_message(kfrag_id: 'KeyFragID',
# Have to convert to bytes manually because `mypy` is not smart enough to resolve types. # Have to convert to bytes manually because `mypy` is not smart enough to resolve types.
delegating_part = ([serialize_bool(True), bytes(maybe_delegating_pk)] delegating_part = ([bool_bytes(True), bytes(maybe_delegating_pk)]
if maybe_delegating_pk if maybe_delegating_pk
else [serialize_bool(False)]) else [bool_bytes(False)])
cast(List[Serializable], delegating_part) cast(List[Serializable], delegating_part)
receiving_part = ([serialize_bool(True), bytes(maybe_receiving_pk)] receiving_part = ([bool_bytes(True), bytes(maybe_receiving_pk)]
if maybe_receiving_pk if maybe_receiving_pk
else [serialize_bool(False)]) else [bool_bytes(False)])
components = ([bytes(kfrag_id), bytes(commitment), bytes(precursor)] + components = ([bytes(kfrag_id), bytes(commitment), bytes(precursor)] +
delegating_part + delegating_part +

View File

@ -1,5 +1,5 @@
import os import os
from typing import List, Optional from typing import List, Optional, Tuple, Type
from .curve_point import CurvePoint from .curve_point import CurvePoint
from .curve_scalar import CurveScalar from .curve_scalar import CurveScalar
@ -7,11 +7,13 @@ from .errors import VerificationError
from .hashing import hash_to_shared_secret, kfrag_signature_message, hash_to_polynomial_arg from .hashing import hash_to_shared_secret, kfrag_signature_message, hash_to_polynomial_arg
from .keys import PublicKey, SecretKey from .keys import PublicKey, SecretKey
from .params import PARAMETERS from .params import PARAMETERS
from .serializable import Serializable, serialize_bool, take_bool from .serializable import (
Serializable, Deserializable, HasSerializedSize,
bool_bytes, bool_serialized_size)
from .signing import Signature, Signer from .signing import Signature, Signer
class KeyFragID(Serializable): class KeyFragID(Serializable, Deserializable):
__SIZE = 32 __SIZE = 32
@ -26,15 +28,18 @@ class KeyFragID(Serializable):
return cls(os.urandom(cls.__SIZE)) return cls(os.urandom(cls.__SIZE))
@classmethod @classmethod
def __take__(cls, data): def serialized_size(cls):
id_, data = cls.__take_bytes__(data, cls.__SIZE) return cls.__SIZE
return cls(id_), data
@classmethod
def _from_exact_bytes(cls, data):
return cls(data)
def __bytes__(self): def __bytes__(self):
return self._id return self._id
class KeyFragProof(Serializable): class KeyFragProof(Serializable, Deserializable):
@classmethod @classmethod
def from_base(cls, def from_base(cls,
@ -102,22 +107,25 @@ class KeyFragProof(Serializable):
def __eq__(self, other): def __eq__(self, other):
return self._components() == other._components() return self._components() == other._components()
@classmethod _SERIALIZED_SIZE = (CurvePoint.serialized_size() +
def __take__(cls, data): Signature.serialized_size() * 2 +
types = [CurvePoint, Signature, Signature] bool_serialized_size() * 2)
(commitment, sig_proxy, sig_bob), data = cls.__take_types__(data, *types)
delegating_key_signed, data = take_bool(data)
receiving_key_signed, data = take_bool(data)
obj = cls(commitment, sig_proxy, sig_bob, delegating_key_signed, receiving_key_signed) @classmethod
return obj, data def serialized_size(cls):
return cls._SERIALIZED_SIZE
@classmethod
def _from_exact_bytes(cls, data):
types = [CurvePoint, Signature, Signature, bool, bool]
return cls(*cls._split(data, *types))
def __bytes__(self): def __bytes__(self):
return (bytes(self.commitment) + return (bytes(self.commitment) +
bytes(self.signature_for_proxy) + bytes(self.signature_for_proxy) +
bytes(self.signature_for_receiver) + bytes(self.signature_for_receiver) +
serialize_bool(self.delegating_key_signed) + bool_bytes(self.delegating_key_signed) +
serialize_bool(self.receiving_key_signed) bool_bytes(self.receiving_key_signed)
) )
@ -129,7 +137,7 @@ def poly_eval(coeffs: List[CurveScalar], x: CurveScalar) -> CurveScalar:
return result return result
class KeyFrag(Serializable): class KeyFrag(Serializable, Deserializable):
""" """
A signed fragment of the delegating key. A signed fragment of the delegating key.
""" """
@ -144,18 +152,24 @@ class KeyFrag(Serializable):
self.precursor = precursor self.precursor = precursor
self.proof = proof self.proof = proof
@classmethod
def __take__(cls, data):
types = [KeyFragID, CurveScalar, CurvePoint, KeyFragProof]
components, data = cls.__take_types__(data, *types)
return cls(*components), data
def __bytes__(self):
return bytes(self.id) + bytes(self.key) + bytes(self.precursor) + bytes(self.proof)
def _components(self): def _components(self):
return self.id, self.key, self.precursor, self.proof return self.id, self.key, self.precursor, self.proof
_COMPONENT_TYPES: Tuple[Type[HasSerializedSize], ...] = (
KeyFragID, CurveScalar, CurvePoint, KeyFragProof)
_SERIALIZED_SIZE = sum(tp.serialized_size() for tp in _COMPONENT_TYPES)
@classmethod
def serialized_size(cls):
return cls._SERIALIZED_SIZE
@classmethod
def _from_exact_bytes(cls, data):
return cls(*cls._split(data, *cls._COMPONENT_TYPES))
def __bytes__(self):
return b''.join(bytes(comp) for comp in self._components())
def __eq__(self, other): def __eq__(self, other):
return self._components() == other._components() return self._components() == other._components()
@ -240,7 +254,7 @@ class KeyFrag(Serializable):
return VerifiedKeyFrag(self) return VerifiedKeyFrag(self)
class VerifiedKeyFrag: class VerifiedKeyFrag(Serializable):
""" """
Verified kfrag, good for reencryption. Verified kfrag, good for reencryption.
Can be cast to ``bytes``, but cannot be deserialized from bytes directly. Can be cast to ``bytes``, but cannot be deserialized from bytes directly.
@ -253,6 +267,22 @@ class VerifiedKeyFrag:
def __bytes__(self): def __bytes__(self):
return bytes(self.kfrag) return bytes(self.kfrag)
@classmethod
def serialized_size(cls):
return KeyFrag.serialized_size()
@classmethod
def from_verified_bytes(cls, data) -> 'VerifiedKeyFrag':
"""
Restores a verified keyfrag directly from serialized bytes,
skipping :py:meth:`KeyFrag.verify` call.
Intended for internal storage;
make sure that the bytes come from a trusted source.
"""
kfrag = KeyFrag.from_bytes(data)
return cls(kfrag)
def __eq__(self, other): def __eq__(self, other):
return self.kfrag == other.kfrag return self.kfrag == other.kfrag

View File

@ -5,10 +5,10 @@ from .curve_scalar import CurveScalar
from .curve_point import CurvePoint from .curve_point import CurvePoint
from .dem import kdf from .dem import kdf
from .hashing import Hash from .hashing import Hash
from .serializable import Serializable from .serializable import Serializable, Deserializable
class SecretKey(Serializable): class SecretKey(Serializable, Deserializable):
""" """
Umbral secret (private) key. Umbral secret (private) key.
""" """
@ -43,15 +43,18 @@ class SecretKey(Serializable):
return self._scalar_key return self._scalar_key
@classmethod @classmethod
def __take__(cls, data: bytes) -> Tuple['SecretKey', bytes]: def serialized_size(cls):
(scalar_key,), data = cls.__take_types__(data, CurveScalar) return CurveScalar.serialized_size()
return cls(scalar_key), data
@classmethod
def _from_exact_bytes(cls, data: bytes):
return cls(CurveScalar._from_exact_bytes(data))
def __bytes__(self) -> bytes: def __bytes__(self) -> bytes:
return bytes(self._scalar_key) return bytes(self._scalar_key)
class PublicKey(Serializable): class PublicKey(Serializable, Deserializable):
""" """
Umbral public key. Umbral public key.
""" """
@ -70,9 +73,12 @@ class PublicKey(Serializable):
return cls(sk._public_key_point) return cls(sk._public_key_point)
@classmethod @classmethod
def __take__(cls, data: bytes) -> Tuple['PublicKey', bytes]: def serialized_size(cls):
(point_key,), data = cls.__take_types__(data, CurvePoint) return CurvePoint.serialized_size()
return cls(point_key), data
@classmethod
def _from_exact_bytes(cls, data: bytes):
return cls(CurvePoint._from_exact_bytes(data))
def __bytes__(self) -> bytes: def __bytes__(self) -> bytes:
return bytes(self._point_key) return bytes(self._point_key)
@ -87,7 +93,7 @@ class PublicKey(Serializable):
return hash((self.__class__, bytes(self))) return hash((self.__class__, bytes(self)))
class SecretKeyFactory(Serializable): class SecretKeyFactory(Serializable, Deserializable):
""" """
This class handles keyring material for Umbral, by allowing deterministic This class handles keyring material for Umbral, by allowing deterministic
derivation of :py:class:`SecretKey` objects based on labels. derivation of :py:class:`SecretKey` objects based on labels.
@ -122,9 +128,12 @@ class SecretKeyFactory(Serializable):
return SecretKey(scalar_key) return SecretKey(scalar_key)
@classmethod @classmethod
def __take__(cls, data: bytes) -> Tuple['SecretKeyFactory', bytes]: def serialized_size(cls):
key_seed, data = cls.__take_bytes__(data, cls._KEY_SEED_SIZE) return cls._KEY_SEED_SIZE
return cls(key_seed), data
@classmethod
def _from_exact_bytes(cls, data: bytes):
return cls(data)
def __bytes__(self) -> bytes: def __bytes__(self) -> bytes:
return bytes(self.__key_seed) return bytes(self.__key_seed)

View File

@ -8,25 +8,25 @@ from .key_frag import VerifiedKeyFrag, KeyFrag, KeyFragBase
from .signing import Signer from .signing import Signer
def encrypt(pk: PublicKey, plaintext: bytes) -> Tuple[Capsule, bytes]: def encrypt(delegating_pk: PublicKey, plaintext: bytes) -> Tuple[Capsule, bytes]:
""" """
Generates and encapsulates a symmetric key and uses it to encrypt the given plaintext. Generates and encapsulates a symmetric key and uses it to encrypt the given plaintext.
Returns the KEM Capsule and the ciphertext. Returns the KEM Capsule and the ciphertext.
""" """
capsule, key_seed = Capsule.from_public_key(pk) capsule, key_seed = Capsule.from_public_key(delegating_pk)
dem = DEM(bytes(key_seed)) dem = DEM(bytes(key_seed))
ciphertext = dem.encrypt(plaintext, authenticated_data=bytes(capsule)) ciphertext = dem.encrypt(plaintext, authenticated_data=bytes(capsule))
return capsule, ciphertext return capsule, ciphertext
def decrypt_original(sk: SecretKey, capsule: Capsule, ciphertext: bytes) -> bytes: def decrypt_original(delegating_sk: SecretKey, capsule: Capsule, ciphertext: bytes) -> bytes:
""" """
Opens the capsule using the original (Alice's) key used for encryption and gets what's inside. Opens the capsule using the delegator's key used for encryption and gets what's inside.
We hope that's a symmetric key, which we use to decrypt the ciphertext We hope that's a symmetric key, which we use to decrypt the ciphertext
and return the resulting cleartext. and return the resulting cleartext.
""" """
key_seed = capsule.open_original(sk) key_seed = capsule.open_original(delegating_sk)
dem = DEM(bytes(key_seed)) dem = DEM(bytes(key_seed))
return dem.decrypt(ciphertext, authenticated_data=bytes(capsule)) return dem.decrypt(ciphertext, authenticated_data=bytes(capsule))
@ -60,26 +60,20 @@ def generate_kfrags(delegating_sk: SecretKey,
return [VerifiedKeyFrag(kfrag) for kfrag in kfrags] return [VerifiedKeyFrag(kfrag) for kfrag in kfrags]
def reencrypt(capsule: Capsule, def reencrypt(capsule: Capsule, kfrag: VerifiedKeyFrag) -> VerifiedCapsuleFrag:
kfrag: VerifiedKeyFrag,
metadata: Optional[bytes] = None
) -> VerifiedCapsuleFrag:
""" """
Creates a capsule fragment using the given key fragment. Creates a capsule fragment using the given key fragment.
Capsule fragments can later be used to decrypt the ciphertext. Capsule fragments can later be used to decrypt the ciphertext.
If `metadata` is provided, it will have to be used for verification in
:py:meth:`CapsuleFrag.verify`.
""" """
# We could let duck typing do its work, # We could let duck typing do its work,
# but it's better to make a common error more understandable. # but it's better to make a common error more understandable.
if isinstance(kfrag, KeyFrag) and not isinstance(kfrag, VerifiedKeyFrag): if isinstance(kfrag, KeyFrag) and not isinstance(kfrag, VerifiedKeyFrag):
raise TypeError("KeyFrag must be verified before reencryption") raise TypeError("KeyFrag must be verified before reencryption")
return VerifiedCapsuleFrag(CapsuleFrag.reencrypted(capsule, kfrag.kfrag, metadata)) return VerifiedCapsuleFrag(CapsuleFrag.reencrypted(capsule, kfrag.kfrag))
def decrypt_reencrypted(decrypting_sk: SecretKey, def decrypt_reencrypted(receiving_sk: SecretKey,
delegating_pk: PublicKey, delegating_pk: PublicKey,
capsule: Capsule, capsule: Capsule,
verified_cfrags: Sequence[VerifiedCapsuleFrag], verified_cfrags: Sequence[VerifiedCapsuleFrag],
@ -95,6 +89,6 @@ def decrypt_reencrypted(decrypting_sk: SecretKey,
raise TypeError("All CapsuleFrags must be verified before decryption") raise TypeError("All CapsuleFrags must be verified before decryption")
cfrags = [vcfrag.cfrag for vcfrag in verified_cfrags] cfrags = [vcfrag.cfrag for vcfrag in verified_cfrags]
key_seed = capsule.open_reencrypted(decrypting_sk, delegating_pk, cfrags) key_seed = capsule.open_reencrypted(receiving_sk, delegating_pk, cfrags)
dem = DEM(bytes(key_seed)) dem = DEM(bytes(key_seed))
return dem.decrypt(ciphertext, authenticated_data=bytes(capsule)) return dem.decrypt(ciphertext, authenticated_data=bytes(capsule))

View File

@ -2,56 +2,81 @@ from abc import abstractmethod, ABC
from typing import Tuple, Type, List, Any, TypeVar from typing import Tuple, Type, List, Any, TypeVar
class Serializable(ABC): class HasSerializedSize(ABC):
""" """
A mixin for composable serialization. A base serialization mixin, denoting a type with a constant-size serialized representation.
""" """
_T = TypeVar('_T', bound='Serializable') @classmethod
@abstractmethod
def serialized_size(cls) -> int:
"""
Returns the size in bytes of the serialized representation of this object
(obtained with ``bytes()``).
"""
raise NotImplementedError
class Deserializable(HasSerializedSize):
"""
A mixin for composable deserialization.
"""
_T = TypeVar('_T', bound='Deserializable')
@classmethod @classmethod
def from_bytes(cls: Type[_T], data: bytes) -> _T: def from_bytes(cls: Type[_T], data: bytes) -> _T:
""" """
Restores the object from serialized bytes. Restores the object from serialized bytes.
""" """
obj, remainder = cls.__take__(data) expected_size = cls.serialized_size()
if len(remainder) != 0: if len(data) != expected_size:
raise ValueError(f"{len(remainder)} bytes remaining after deserializing {cls}") raise ValueError(f"Expected {expected_size} bytes, got {len(data)}")
return obj return cls._from_exact_bytes(data)
@classmethod @staticmethod
def __take_bytes__(cls, data: bytes, size: int) -> Tuple[bytes, bytes]: def _split(data: bytes, *types: Type) -> List[Any]:
""" """
Takes ``size`` bytes from the bytestring and returns them along with the remainder. Given a list of ``Deserializable`` types, attempts to deserialize them from the bytestring
"""
if len(data) < size:
raise ValueError(f"{cls} cannot take {size} bytes "
f"from a bytestring of size {len(data)}")
return data[:size], data[size:]
@classmethod
def __take_types__(cls, data: bytes, *types: Type) -> Tuple[List[Any], bytes]:
"""
Given a list of ``Serializable`` types, attempts to deserialize them from the bytestring
one by one and returns the list of the resulting objects and the remaining bytestring. one by one and returns the list of the resulting objects and the remaining bytestring.
""" """
objs = [] objs = []
pos = 0
for tp in types: for tp in types:
obj, data = tp.__take__(data)
if issubclass(tp, bool):
size = bool_serialized_size()
else:
size = tp.serialized_size()
chunk = data[pos:pos+size]
if issubclass(tp, bool):
obj = bool_from_exact_bytes(chunk)
else:
obj = tp._from_exact_bytes(chunk)
objs.append(obj) objs.append(obj)
return objs, data pos += size
return objs
@classmethod @classmethod
@abstractmethod @abstractmethod
def __take__(cls: Type[_T], data: bytes) -> Tuple[_T, bytes]: def _from_exact_bytes(cls: Type[_T], data: bytes) -> _T:
""" """
Take however much is necessary from ``data`` and instantiate the object, Deserializes the object from a bytestring of exactly the expected length
returning it and the remaining bytestring. (defined by ``serialized_size()``).
Must be implemented by the derived class.
""" """
raise NotImplementedError raise NotImplementedError
class Serializable(HasSerializedSize):
"""
A mixin for composable serialization.
"""
@abstractmethod @abstractmethod
def __bytes__(self): def __bytes__(self):
""" """
@ -60,17 +85,20 @@ class Serializable(ABC):
raise NotImplementedError raise NotImplementedError
def serialize_bool(b: bool) -> bytes: def bool_serialized_size() -> int:
return 1
def bool_bytes(b: bool) -> bytes:
return b'\x01' if b else b'\x00' return b'\x01' if b else b'\x00'
def take_bool(data: bytes) -> Tuple[bool, bytes]: def bool_from_exact_bytes(data: bytes) -> bool:
bool_bytes, data = Serializable.__take_bytes__(data, 1) if data == b'\x01':
if bool_bytes == b'\x01':
b = True b = True
elif bool_bytes == b'\x00': elif data == b'\x00':
b = False b = False
else: else:
raise ValueError("Incorrectly serialized boolean; " raise ValueError("Incorrectly serialized boolean; "
f"expected b'\\x00' or b'\\x01', got {repr(bool_bytes)}") f"expected b'\\x00' or b'\\x01', got {repr(data)}")
return b, data return b

View File

@ -3,7 +3,7 @@ from .curve import CURVE
from .curve_scalar import CurveScalar from .curve_scalar import CurveScalar
from .hashing import Hash from .hashing import Hash
from .keys import SecretKey, PublicKey from .keys import SecretKey, PublicKey
from .serializable import Serializable from .serializable import Serializable, Deserializable
def digest_for_signing(message: bytes) -> Hash: def digest_for_signing(message: bytes) -> Hash:
@ -66,7 +66,7 @@ class Signer:
raise RuntimeError(f"{self.__class__.__name__} objects do not support serialization") raise RuntimeError(f"{self.__class__.__name__} objects do not support serialization")
class Signature(Serializable): class Signature(Serializable, Deserializable):
""" """
Wrapper for ECDSA signatures. Wrapper for ECDSA signatures.
""" """
@ -92,9 +92,12 @@ class Signature(Serializable):
return self.verify_digest(verifying_key, digest) return self.verify_digest(verifying_key, digest)
@classmethod @classmethod
def __take__(cls, data): def serialized_size(cls):
(r, s), data = cls.__take_types__(data, CurveScalar, CurveScalar) return CurveScalar.serialized_size() * 2
return cls(r, s), data
@classmethod
def _from_exact_bytes(cls, data: bytes):
return cls(*cls._split(data, CurveScalar, CurveScalar))
def __bytes__(self): def __bytes__(self):
return bytes(self.r) + bytes(self.s) return bytes(self.r) + bytes(self.s)

View File

@ -237,9 +237,8 @@ create_test_vector_file(vector_suite, 'vectors_kfrags.json', generate_again=gene
vectors = list() vectors = list()
metadata = b'kfrag_metadata'
for kfrag in kfrags: for kfrag in kfrags:
cfrag = reencrypt(capsule, kfrag, metadata) cfrag = reencrypt(capsule, kfrag)
json_input = {'kfrag': hexlify(kfrag), 'cfrag': hexlify(cfrag)} json_input = {'kfrag': hexlify(kfrag), 'cfrag': hexlify(cfrag)}
vectors.append(json_input) vectors.append(json_input)
@ -249,10 +248,9 @@ vector_suite = {
'enclosed Capsule, under the enclosed delegating, ' 'enclosed Capsule, under the enclosed delegating, '
'verifying and receiving keys. Each CFrag must deserialize ' 'verifying and receiving keys. Each CFrag must deserialize '
'correctly and can be replicated with a call to ' 'correctly and can be replicated with a call to '
'`reencrypt(kfrag, capsule, , b\'kfrag_metadata\')`'), '`reencrypt(kfrag, capsule)`'),
'params': 'default', 'params': 'default',
'capsule': hexlify(capsule), 'capsule': hexlify(capsule),
'metadata': hexlify(metadata),
'verifying_pk': hexlify(verifying_pk), 'verifying_pk': hexlify(verifying_pk),
'delegating_pk': hexlify(delegating_pk), 'delegating_pk': hexlify(delegating_pk),
'receiving_pk': hexlify(receiving_pk), 'receiving_pk': hexlify(receiving_pk),

View File

@ -1,9 +1,8 @@
{ {
"name": "Test vectors for CFrags", "name": "Test vectors for CFrags",
"description": "This is a collection of CFrags, originated from the enclosed Capsule, under the enclosed delegating, verifying and receiving keys. Each CFrag must deserialize correctly and can be replicated with a call to `reencrypt(kfrag, capsule, , b'kfrag_metadata')`", "description": "This is a collection of CFrags, originated from the enclosed Capsule, under the enclosed delegating, verifying and receiving keys. Each CFrag must deserialize correctly and can be replicated with a call to `reencrypt(kfrag, capsule)`",
"params": "default", "params": "default",
"capsule": "02558f1de19a58e73a94e8fbbc6d3b1de2d312d90746ea74cb29f046943bf5787102906780e9484aec2102a01a157f10ced5aec952cd00631d94d5ea2edfa9b6808361b109353b0827b7e4013ab92a70eb3337a37f6fe34b3ccb058592caa246c974", "capsule": "02558f1de19a58e73a94e8fbbc6d3b1de2d312d90746ea74cb29f046943bf5787102906780e9484aec2102a01a157f10ced5aec952cd00631d94d5ea2edfa9b6808361b109353b0827b7e4013ab92a70eb3337a37f6fe34b3ccb058592caa246c974",
"metadata": "6b667261675f6d65746164617461",
"verifying_pk": "030b95b3f249297824b32d3391392d62a9aff32e8698fa78c7e8ce4a9d17071f56", "verifying_pk": "030b95b3f249297824b32d3391392d62a9aff32e8698fa78c7e8ce4a9d17071f56",
"delegating_pk": "02d67029bb92522059225d190038230c23466e28d132d48f714f9098168a562b8a", "delegating_pk": "02d67029bb92522059225d190038230c23466e28d132d48f714f9098168a562b8a",
"receiving_pk": "03b0d0243e8954b408047eee3b09b5ed132ccc25ec70e99fc74b6e9f54e5ecf9c7", "receiving_pk": "03b0d0243e8954b408047eee3b09b5ed132ccc25ec70e99fc74b6e9f54e5ecf9c7",
@ -49,4 +48,4 @@
"cfrag": "03a3e0b6b2fa9f634acac1d4ed682dbf0c08ba27889087854059d80d021797d7b403a64ced5bc32ad0cf5746eb5ab1ebdc12a1d1bbbd0f45d0fc90c3e4b56ba70546df55683a69e4136205a61c895959d86eb64840bb2175d5d15e4491b27e6a38bc036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b02ef471b06988f07ce34c7bfa5825603eedbde4138c05296055cff54f6204d245a02119417e7aa9c207a71540abefb8de8e04c977db7d11a82e2f98a1ea3fff5519e03ab6ffe8768ecd246956362065ca48488183f96c3ee5957be7a7ce02f3ef9cb1202e8febd066a999b83b4441662636c6c978cb8789553914b5ed94c02d4784432d74e9fffcb5ce10b65d95624b23940415f0dbe29e174a6b73c78cb3d5d380d8882ae3cc293a58554a55dbc50c0e111c6e9bb0208631833da5e66b1adb2aa1ecdd615c09098fba7af4b03763b846e1f5358a6bac27c36eadd6cd2c98fd58c277e0b" "cfrag": "03a3e0b6b2fa9f634acac1d4ed682dbf0c08ba27889087854059d80d021797d7b403a64ced5bc32ad0cf5746eb5ab1ebdc12a1d1bbbd0f45d0fc90c3e4b56ba70546df55683a69e4136205a61c895959d86eb64840bb2175d5d15e4491b27e6a38bc036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b02ef471b06988f07ce34c7bfa5825603eedbde4138c05296055cff54f6204d245a02119417e7aa9c207a71540abefb8de8e04c977db7d11a82e2f98a1ea3fff5519e03ab6ffe8768ecd246956362065ca48488183f96c3ee5957be7a7ce02f3ef9cb1202e8febd066a999b83b4441662636c6c978cb8789553914b5ed94c02d4784432d74e9fffcb5ce10b65d95624b23940415f0dbe29e174a6b73c78cb3d5d380d8882ae3cc293a58554a55dbc50c0e111c6e9bb0208631833da5e66b1adb2aa1ecdd615c09098fba7af4b03763b846e1f5358a6bac27c36eadd6cd2c98fd58c277e0b"
} }
] ]
} }