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 = "*"
pytest-mypy = "*"
pytest-mock = "*"
pytest-cov = "*"
pytest-benchmark = {version = "*",extras = ["histogram"]}
# Pytest Plugin Subdeps
mock = "*"
mypy = "*"
monkeytype = "*"
coverage = "*"
codecov = "*"
# Testing libraries
hypothesis = "*"
# Testing libraries
nbval = "*"
# Docs
sphinx = "*"
sphinx-autobuild = "*"
sphinx_rtd_theme = "*"
# Overrides vulnerable versions allowed by codecov and sphinx:
# Overrides vulnerable versions allowed by codecov and sphinx:
requests = ">=2.20.0"
[pipenv]

501
Pipfile.lock generated
View File

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

View File

@ -147,7 +147,7 @@ and then decrypts the re-encrypted ciphertext.
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,
capsule=capsule,
cfrags=cfrags,

View File

@ -1,6 +1,6 @@
import random
from umbral import (
SecretKey, PublicKey, Signer, GenericError, CapsuleFrag,
SecretKey, PublicKey, Signer, CapsuleFrag,
encrypt, generate_kfrags, reencrypt, decrypt_original, decrypt_reencrypted)
# Generate an Umbral key pair
@ -46,7 +46,7 @@ bob_capsule = capsule
# Attempt Bob's decryption (fail)
try:
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.")
# 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.
bob_cleartext = decrypt_reencrypted(decrypting_sk=bobs_secret_key,
bob_cleartext = decrypt_reencrypted(receiving_sk=bobs_secret_key,
delegating_pk=alices_public_key,
capsule=bob_capsule,
verified_cfrags=cfrags,

View File

@ -94,7 +94,7 @@
"from umbral import decrypt_original\n",
"\n",
"\n",
"cleartext = decrypt_original(sk=alices_private_key,\n",
"cleartext = decrypt_original(delegating_sk=alices_private_key,\n",
" capsule=capsule,\n",
" ciphertext=ciphertext)\n",
"print(cleartext)"
@ -141,13 +141,11 @@
}
],
"source": [
"from umbral import GenericError\n",
"\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",
" ciphertext=ciphertext)\n",
"except GenericError:\n",
"except ValueError:\n",
" print(\"Decryption failed! Bob doesn't has access granted yet.\")\n"
]
},
@ -266,7 +264,7 @@
"source": [
"from umbral import decrypt_reencrypted\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",
" capsule=capsule,\n",
" verified_cfrags=cfrags,\n",

View File

@ -40,7 +40,9 @@ Intermediate objects
:show-inheritance:
.. autoclass:: VerifiedKeyFrag()
:members:
:special-members: __eq__, __hash__
:show-inheritance:
.. autoclass:: CapsuleFrag()
:members:
@ -49,6 +51,7 @@ Intermediate objects
.. autoclass:: VerifiedCapsuleFrag()
:special-members: __eq__, __hash__
:show-inheritance:
Encryption, re-encryption and decryption
----------------------------------------
@ -66,12 +69,16 @@ Encryption, re-encryption and decryption
Utilities
---------
.. autoclass:: umbral.GenericError
:show-inheritance:
.. autoclass:: umbral.VerificationError
:show-inheritance:
.. autoclass:: umbral.serializable.HasSerializedSize
:members: serialized_size
.. autoclass:: umbral.serializable.Serializable
:members: from_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
>>> fail = decrypt_original(sk=bobs_secret_key,
>>> fail = decrypt_original(delegating_sk=bobs_secret_key,
... capsule=capsule,
... ciphertext=ciphertext)
Traceback (most recent call last):
...
umbral.GenericError
ValueError
Ursulas perform re-encryption
@ -196,7 +196,7 @@ Finally, Bob decrypts the re-encrypted ciphertext using his key.
.. doctest:: capsule_story
>>> 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,
... capsule=capsule,
... verified_cfrags=cfrags,

View File

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

View File

@ -5,7 +5,6 @@ from umbral import (
SecretKey,
PublicKey,
Signer,
GenericError,
encrypt,
decrypt_original,
reencrypt,
@ -29,7 +28,7 @@ def test_capsule_serialization(alices_keys):
capsule.point_e = CurvePoint.random()
capsule_bytes = bytes(capsule)
with pytest.raises(GenericError):
with pytest.raises(ValueError):
Capsule.from_bytes(capsule_bytes)
@ -86,7 +85,7 @@ def test_open_reencrypted(alices_keys, bobs_keys):
capsule.open_reencrypted(receiving_sk, delegating_pk, [])
# 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])
# Repeating cfrags

View File

@ -1,6 +1,6 @@
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
@ -8,9 +8,8 @@ def test_cfrag_serialization(verification_keys, capsule, kfrags):
verifying_pk, delegating_pk, receiving_pk = verification_keys
metadata = b'This is an example of metadata for re-encryption request'
for kfrag in kfrags:
cfrag = reencrypt(capsule, kfrag, metadata=metadata)
cfrag = reencrypt(capsule, kfrag)
cfrag_bytes = bytes(cfrag)
new_cfrag = CapsuleFrag.from_bytes(cfrag_bytes)
@ -19,34 +18,15 @@ def test_cfrag_serialization(verification_keys, capsule, kfrags):
verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
metadata=metadata,
)
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
with pytest.raises(VerificationError):
new_cfrag.verify(capsule,
verifying_pk=verifying_pk,
delegating_pk=receiving_pk,
receiving_pk=receiving_pk,
metadata=metadata,
)
# Wrong receiving key
@ -55,7 +35,6 @@ def test_cfrag_serialization(verification_keys, capsule, kfrags):
verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=delegating_pk,
metadata=metadata,
)
# Wrong signing key
@ -64,34 +43,6 @@ def test_cfrag_serialization(verification_keys, capsule, kfrags):
verifying_pk=receiving_pk,
delegating_pk=delegating_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_alice2, _unused_key2 = Capsule.from_public_key(delegating_pk)
metadata = b"some metadata"
cfrag = reencrypt(capsule_alice2, kfrags[0], metadata=metadata)
cfrag = reencrypt(capsule_alice2, kfrags[0])
cfrag = CapsuleFrag.from_bytes(bytes(cfrag)) # de-verify
with pytest.raises(VerificationError):
@ -112,7 +62,6 @@ def test_cfrag_with_wrong_capsule(verification_keys, kfrags, capsule_and_ciphert
verifying_pk=verifying_pk,
delegating_pk=delegating_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
verifying_pk, delegating_pk, receiving_pk = verification_keys
metadata = b"some metadata"
cfrag = reencrypt(capsule, kfrags[0], metadata=metadata)
cfrag = reencrypt(capsule, kfrags[0])
# Let's put random garbage in one of the cfrags
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,
delegating_pk=delegating_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
cfrag0 = reencrypt(capsule, kfrags[0], metadata=b'abcdef')
cfrag1 = reencrypt(capsule, kfrags[1], metadata=b'abcdef')
cfrag0 = reencrypt(capsule, kfrags[0])
cfrag1 = reencrypt(capsule, kfrags[1])
assert hash(cfrag0) != hash(cfrag1)
@ -154,16 +101,22 @@ def test_cfrag_is_hashable(verification_keys, capsule, kfrags):
verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
metadata=b'abcdef')
)
assert hash(verified_cfrag) == hash(cfrag0)
def test_cfrag_str(capsule, kfrags):
cfrag0 = reencrypt(capsule, kfrags[0], metadata=b'abcdef')
cfrag0 = reencrypt(capsule, kfrags[0])
s = str(cfrag0)
assert 'VerifiedCapsuleFrag' in s
s = str(CapsuleFrag.from_bytes(bytes(cfrag0)))
assert "VerifiedCapsuleFrag" not 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,
capsule_bytes, kfrags_bytes, threshold, metadata):
capsule_bytes, kfrags_bytes, threshold):
capsule = umbral.Capsule.from_bytes(bytes(capsule_bytes))
verified_kfrags = _verify_kfrags(umbral, kfrags_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]
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_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,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
metadata=metadata)
)
for cfrag in cfrags]
# Decryption by Bob
plaintext = umbral.decrypt_reencrypted(decrypting_sk=receiving_sk,
plaintext = umbral.decrypt_reencrypted(receiving_sk=receiving_sk,
delegating_pk=delegating_pk,
capsule=capsule,
verified_cfrags=verified_cfrags,
@ -184,7 +184,6 @@ def test_reencrypt(implementations):
umbral1, umbral2 = implementations
metadata = b'metadata'
threshold = 2
num_kfrags = 3
plaintext = b'peace at dawn'
@ -203,13 +202,13 @@ def test_reencrypt(implementations):
# On client 2
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
plaintext_reencrypted = _decrypt_reencrypted(umbral1,
receiving_sk_bytes, delegating_pk_bytes, verifying_pk_bytes,
capsule_bytes, cfrags_bytes, ciphertext, metadata)
capsule_bytes, cfrags_bytes, ciphertext)
assert plaintext_reencrypted == plaintext
@ -243,3 +242,30 @@ def test_signer(implementations):
assert _verify_message(umbral1, pk_bytes, signature2_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 os
from umbral import GenericError
from umbral.dem import DEM
@ -48,7 +47,7 @@ def test_malformed_ciphertext():
dem.decrypt(ciphertext[:DEM.NONCE_SIZE + DEM.TAG_SIZE - 1])
# Too long
with pytest.raises(GenericError):
with pytest.raises(ValueError):
dem.decrypt(ciphertext + b'abcd')
@ -77,5 +76,5 @@ def test_encrypt_decrypt_associated_data():
assert cleartext1 == plaintext
# Attempt decryption with invalid associated data
with pytest.raises(GenericError):
with pytest.raises(ValueError):
cleartext2 = dem.decrypt(ciphertext0, authenticated_data=b'wrong data')

View File

@ -1,7 +1,7 @@
import pytest
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
@ -124,3 +124,15 @@ def test_kfrag_str(kfrags):
s = str(KeyFrag.from_bytes(bytes(kfrags[0])))
assert "VerifiedKeyFrag" not 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,
PublicKey,
Signer,
GenericError,
KeyFrag,
CapsuleFrag,
encrypt,
@ -25,7 +24,7 @@ def test_public_key_encryption(alices_keys):
# Wrong secret key
sk = SecretKey.random()
with pytest.raises(GenericError):
with pytest.raises(ValueError):
decrypt_original(sk, capsule, ciphertext)
@ -81,7 +80,7 @@ def test_simple_api(num_kfrags, threshold):
cfrags = [reencrypt(capsule, kfrag) for kfrag in kfrags]
# Decryption by Bob
plaintext_reenc = decrypt_reencrypted(decrypting_sk=receiving_sk,
plaintext_reenc = decrypt_reencrypted(receiving_sk=receiving_sk,
delegating_pk=delegating_pk,
capsule=capsule,
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[0] = CapsuleFrag.from_bytes(bytes(cfrags[0]))
with pytest.raises(TypeError):
plaintext_reenc = decrypt_reencrypted(decrypting_sk=receiving_sk,
plaintext_reenc = decrypt_reencrypted(receiving_sk=receiving_sk,
delegating_pk=delegating_pk,
capsule=capsule,
verified_cfrags=cfrags,

View File

@ -2,55 +2,65 @@ import re
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):
assert 0 <= val < 2**32
self.val = val
@classmethod
def __take__(cls, data):
val_bytes, data = cls.__take_bytes__(data, 4)
return cls(int.from_bytes(val_bytes, byteorder='big')), data
def serialized_size(cls):
return 4
@classmethod
def _from_exact_bytes(cls, data):
return cls(int.from_bytes(data, byteorder='big'))
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):
return isinstance(other, A) and self.val == other.val
class B(Serializable):
class B(Serializable, Deserializable):
def __init__(self, val: int):
assert 0 <= val < 2**16
self.val = val
@classmethod
def __take__(cls, data):
val_bytes, data = cls.__take_bytes__(data, 2)
return cls(int.from_bytes(val_bytes, byteorder='big')), data
def serialized_size(cls):
return 2
@classmethod
def _from_exact_bytes(cls, data):
return cls(int.from_bytes(data, byteorder='big'))
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):
return isinstance(other, B) and self.val == other.val
class C(Serializable):
class C(Serializable, Deserializable):
def __init__(self, a: A, b: B):
self.a = a
self.b = b
@classmethod
def __take__(cls, data):
components, data = cls.__take_types__(data, A, B)
return cls(*components), data
def serialized_size(cls):
return A.serialized_size() + B.serialized_size()
@classmethod
def _from_exact_bytes(cls, data):
components = cls._split(data, A, B)
return cls(*components)
def __bytes__(self):
return bytes(self.a) + bytes(self.b)
@ -71,7 +81,7 @@ def test_too_many_bytes():
a = A(2**32 - 123)
b = B(2**16 - 456)
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')
@ -80,13 +90,22 @@ def test_not_enough_bytes():
b = B(2**16 - 456)
c = C(a, b)
# 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])
def test_serialize_bool():
assert take_bool(serialize_bool(True) + b'1234') == (True, b'1234')
assert take_bool(serialize_bool(False) + b'12') == (False, b'12')
def test_bool_bytes():
assert bool_from_exact_bytes(bool_bytes(True)) == True
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'")
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'])))
for json_kfrag in vector_suite['vectors']]
metadata = bytes.fromhex(vector_suite['metadata'])
for kfrag, cfrag in kfrags_n_cfrags:
verified_kfrag = kfrag.verify(verifying_pk=verifying_pk,
delegating_pk=delegating_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_v1 == cfrag.point_v1
assert new_cfrag.kfrag_id == cfrag.kfrag_id
@ -163,4 +161,4 @@ def test_cfrags():
verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
metadata=metadata)
)

View File

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

View File

@ -2,10 +2,9 @@ from typing import TYPE_CHECKING, Tuple, Sequence
from .curve_point import CurvePoint
from .curve_scalar import CurveScalar
from .errors import GenericError
from .hashing import hash_capsule_points, hash_to_polynomial_arg, hash_to_shared_secret
from .keys import PublicKey, SecretKey
from .serializable import Serializable
from .serializable import Serializable, Deserializable
if TYPE_CHECKING: # pragma: no cover
from .capsule_frag import CapsuleFrag
@ -19,7 +18,7 @@ def lambda_coeff(xs: Sequence[CurveScalar], i: int) -> CurveScalar:
return res
class Capsule(Serializable):
class Capsule(Serializable, Deserializable):
"""
Encapsulated symmetric key.
"""
@ -29,21 +28,25 @@ class Capsule(Serializable):
self.point_v = point_v
self.signature = signature
_COMPONENT_TYPES = CurvePoint, CurvePoint, CurveScalar
_SERIALIZED_SIZE = sum(tp.serialized_size() for tp in _COMPONENT_TYPES)
@classmethod
def __take__(cls, data: bytes) -> Tuple['Capsule', bytes]:
(e, v, sig), data = cls.__take_types__(data, CurvePoint, CurvePoint, CurveScalar)
def serialized_size(cls):
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():
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) -> bytes:
def __bytes__(self):
return bytes(self.point_e) + bytes(self.point_v) + bytes(self.signature)
@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()
priv_r = CurveScalar.random_nonzero()
@ -55,12 +58,12 @@ class Capsule(Serializable):
h = hash_capsule_points(pub_r, pub_u)
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
def open_original(self, sk: SecretKey) -> CurvePoint:
return (self.point_e + self.point_v) * sk.secret_scalar()
def open_original(self, delegating_sk: SecretKey) -> CurvePoint:
return (self.point_e + self.point_v) * delegating_sk.secret_scalar()
def open_reencrypted(self,
receiving_sk: SecretKey,
@ -106,7 +109,7 @@ class Capsule(Serializable):
# TODO: check for d == 0? Or just let if fail?
inv_d = d.invert()
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

View File

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

View File

@ -3,10 +3,10 @@ from typing import Tuple
from . import openssl
from .curve import CURVE
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.
"""
@ -34,14 +34,15 @@ class CurvePoint(Serializable):
return openssl.point_to_affine_coords(CURVE, self._backend_point)
@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.
"""
size = CURVE.field_element_size + 1 # compressed point size
point_data, data = cls.__take_bytes__(data, size)
point = openssl.point_from_bytes(CURVE, point_data)
return cls(point), data
return cls(openssl.point_from_bytes(CURVE, data))
def __bytes__(self) -> bytes:
"""

View File

@ -2,12 +2,12 @@ from typing import TYPE_CHECKING, Union, Tuple
from . import openssl
from .curve import CURVE
from .serializable import Serializable
from .serializable import Serializable, Deserializable
if TYPE_CHECKING: # pragma: no cover
from .hashing import Hash
class CurveScalar(Serializable):
class CurveScalar(Serializable, Deserializable):
"""
Represents an OpenSSL Bignum modulo the order of a curve. Some of these
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))
@classmethod
def __take__(cls, data: bytes) -> Tuple['CurveScalar', bytes]:
scalar_data, data = cls.__take_bytes__(data, CURVE.scalar_size)
bignum = openssl.bn_from_bytes(scalar_data, check_modulus=CURVE.bn_order)
return cls(bignum), data
def serialized_size(cls):
return CURVE.scalar_size
@classmethod
def _from_exact_bytes(cls, data: bytes):
return cls(openssl.bn_from_bytes(data, check_modulus=CURVE.bn_order))
def __bytes__(self) -> bytes:
"""

View File

@ -14,7 +14,6 @@ from nacl.bindings.crypto_aead import (
)
from . import openssl
from .errors import GenericError
def kdf(data: bytes,
@ -64,6 +63,6 @@ class DEM:
try:
return xchacha_decrypt(ciphertext, authenticated_data, nonce, self._key)
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 "
"you are using an incorrect decryption key.") from e

View File

@ -1,10 +1,4 @@
class GenericError(Exception):
"""
An interal Umbral error, see the message for details.
"""
class VerificationError(GenericError):
class VerificationError(Exception):
"""
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_scalar import CurveScalar
from .curve_point import CurvePoint
from .serializable import Serializable, serialize_bool
from .serializable import Serializable, bool_bytes
if TYPE_CHECKING: # pragma: no cover
from .key_frag import KeyFragID
@ -63,14 +63,10 @@ def hash_to_shared_secret(precursor: CurvePoint,
return CurveScalar.from_digest(digest)
def hash_to_cfrag_verification(points: Iterable[CurvePoint],
metadata: Optional[bytes] = None
) -> CurveScalar:
def hash_to_cfrag_verification(points: Iterable[CurvePoint]) -> CurveScalar:
digest = Hash(b"CFRAG_VERIFICATION")
for point in points:
digest.update(point)
if metadata is not None:
digest.update(metadata)
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.
delegating_part = ([serialize_bool(True), bytes(maybe_delegating_pk)]
delegating_part = ([bool_bytes(True), bytes(maybe_delegating_pk)]
if maybe_delegating_pk
else [serialize_bool(False)])
else [bool_bytes(False)])
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
else [serialize_bool(False)])
else [bool_bytes(False)])
components = ([bytes(kfrag_id), bytes(commitment), bytes(precursor)] +
delegating_part +

View File

@ -1,5 +1,5 @@
import os
from typing import List, Optional
from typing import List, Optional, Tuple, Type
from .curve_point import CurvePoint
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 .keys import PublicKey, SecretKey
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
class KeyFragID(Serializable):
class KeyFragID(Serializable, Deserializable):
__SIZE = 32
@ -26,15 +28,18 @@ class KeyFragID(Serializable):
return cls(os.urandom(cls.__SIZE))
@classmethod
def __take__(cls, data):
id_, data = cls.__take_bytes__(data, cls.__SIZE)
return cls(id_), data
def serialized_size(cls):
return cls.__SIZE
@classmethod
def _from_exact_bytes(cls, data):
return cls(data)
def __bytes__(self):
return self._id
class KeyFragProof(Serializable):
class KeyFragProof(Serializable, Deserializable):
@classmethod
def from_base(cls,
@ -102,22 +107,25 @@ class KeyFragProof(Serializable):
def __eq__(self, other):
return self._components() == other._components()
@classmethod
def __take__(cls, data):
types = [CurvePoint, Signature, Signature]
(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)
_SERIALIZED_SIZE = (CurvePoint.serialized_size() +
Signature.serialized_size() * 2 +
bool_serialized_size() * 2)
obj = cls(commitment, sig_proxy, sig_bob, delegating_key_signed, receiving_key_signed)
return obj, data
@classmethod
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):
return (bytes(self.commitment) +
bytes(self.signature_for_proxy) +
bytes(self.signature_for_receiver) +
serialize_bool(self.delegating_key_signed) +
serialize_bool(self.receiving_key_signed)
bool_bytes(self.delegating_key_signed) +
bool_bytes(self.receiving_key_signed)
)
@ -129,7 +137,7 @@ def poly_eval(coeffs: List[CurveScalar], x: CurveScalar) -> CurveScalar:
return result
class KeyFrag(Serializable):
class KeyFrag(Serializable, Deserializable):
"""
A signed fragment of the delegating key.
"""
@ -144,18 +152,24 @@ class KeyFrag(Serializable):
self.precursor = precursor
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):
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):
return self._components() == other._components()
@ -240,7 +254,7 @@ class KeyFrag(Serializable):
return VerifiedKeyFrag(self)
class VerifiedKeyFrag:
class VerifiedKeyFrag(Serializable):
"""
Verified kfrag, good for reencryption.
Can be cast to ``bytes``, but cannot be deserialized from bytes directly.
@ -253,6 +267,22 @@ class VerifiedKeyFrag:
def __bytes__(self):
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):
return self.kfrag == other.kfrag

View File

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

View File

@ -8,25 +8,25 @@ from .key_frag import VerifiedKeyFrag, KeyFrag, KeyFragBase
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.
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))
ciphertext = dem.encrypt(plaintext, authenticated_data=bytes(capsule))
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
and return the resulting cleartext.
"""
key_seed = capsule.open_original(sk)
key_seed = capsule.open_original(delegating_sk)
dem = DEM(bytes(key_seed))
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]
def reencrypt(capsule: Capsule,
kfrag: VerifiedKeyFrag,
metadata: Optional[bytes] = None
) -> VerifiedCapsuleFrag:
def reencrypt(capsule: Capsule, kfrag: VerifiedKeyFrag) -> VerifiedCapsuleFrag:
"""
Creates a capsule fragment using the given key fragment.
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,
# but it's better to make a common error more understandable.
if isinstance(kfrag, KeyFrag) and not isinstance(kfrag, VerifiedKeyFrag):
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,
capsule: Capsule,
verified_cfrags: Sequence[VerifiedCapsuleFrag],
@ -95,6 +89,6 @@ def decrypt_reencrypted(decrypting_sk: SecretKey,
raise TypeError("All CapsuleFrags must be verified before decryption")
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))
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
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
def from_bytes(cls: Type[_T], data: bytes) -> _T:
"""
Restores the object from serialized bytes.
"""
obj, remainder = cls.__take__(data)
if len(remainder) != 0:
raise ValueError(f"{len(remainder)} bytes remaining after deserializing {cls}")
return obj
expected_size = cls.serialized_size()
if len(data) != expected_size:
raise ValueError(f"Expected {expected_size} bytes, got {len(data)}")
return cls._from_exact_bytes(data)
@classmethod
def __take_bytes__(cls, data: bytes, size: int) -> Tuple[bytes, bytes]:
@staticmethod
def _split(data: bytes, *types: Type) -> List[Any]:
"""
Takes ``size`` bytes from the bytestring and returns them along with the remainder.
"""
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
Given a list of ``Deserializable`` types, attempts to deserialize them from the bytestring
one by one and returns the list of the resulting objects and the remaining bytestring.
"""
objs = []
pos = 0
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)
return objs, data
pos += size
return objs
@classmethod
@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,
returning it and the remaining bytestring.
Must be implemented by the derived class.
Deserializes the object from a bytestring of exactly the expected length
(defined by ``serialized_size()``).
"""
raise NotImplementedError
class Serializable(HasSerializedSize):
"""
A mixin for composable serialization.
"""
@abstractmethod
def __bytes__(self):
"""
@ -60,17 +85,20 @@ class Serializable(ABC):
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'
def take_bool(data: bytes) -> Tuple[bool, bytes]:
bool_bytes, data = Serializable.__take_bytes__(data, 1)
if bool_bytes == b'\x01':
def bool_from_exact_bytes(data: bytes) -> bool:
if data == b'\x01':
b = True
elif bool_bytes == b'\x00':
elif data == b'\x00':
b = False
else:
raise ValueError("Incorrectly serialized boolean; "
f"expected b'\\x00' or b'\\x01', got {repr(bool_bytes)}")
return b, data
f"expected b'\\x00' or b'\\x01', got {repr(data)}")
return b

View File

@ -3,7 +3,7 @@ from .curve import CURVE
from .curve_scalar import CurveScalar
from .hashing import Hash
from .keys import SecretKey, PublicKey
from .serializable import Serializable
from .serializable import Serializable, Deserializable
def digest_for_signing(message: bytes) -> Hash:
@ -66,7 +66,7 @@ class Signer:
raise RuntimeError(f"{self.__class__.__name__} objects do not support serialization")
class Signature(Serializable):
class Signature(Serializable, Deserializable):
"""
Wrapper for ECDSA signatures.
"""
@ -92,9 +92,12 @@ class Signature(Serializable):
return self.verify_digest(verifying_key, digest)
@classmethod
def __take__(cls, data):
(r, s), data = cls.__take_types__(data, CurveScalar, CurveScalar)
return cls(r, s), data
def serialized_size(cls):
return CurveScalar.serialized_size() * 2
@classmethod
def _from_exact_bytes(cls, data: bytes):
return cls(*cls._split(data, CurveScalar, CurveScalar))
def __bytes__(self):
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()
metadata = b'kfrag_metadata'
for kfrag in kfrags:
cfrag = reencrypt(capsule, kfrag, metadata)
cfrag = reencrypt(capsule, kfrag)
json_input = {'kfrag': hexlify(kfrag), 'cfrag': hexlify(cfrag)}
vectors.append(json_input)
@ -249,10 +248,9 @@ vector_suite = {
'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\')`'),
'`reencrypt(kfrag, capsule)`'),
'params': 'default',
'capsule': hexlify(capsule),
'metadata': hexlify(metadata),
'verifying_pk': hexlify(verifying_pk),
'delegating_pk': hexlify(delegating_pk),
'receiving_pk': hexlify(receiving_pk),

View File

@ -1,9 +1,8 @@
{
"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",
"capsule": "02558f1de19a58e73a94e8fbbc6d3b1de2d312d90746ea74cb29f046943bf5787102906780e9484aec2102a01a157f10ced5aec952cd00631d94d5ea2edfa9b6808361b109353b0827b7e4013ab92a70eb3337a37f6fe34b3ccb058592caa246c974",
"metadata": "6b667261675f6d65746164617461",
"verifying_pk": "030b95b3f249297824b32d3391392d62a9aff32e8698fa78c7e8ce4a9d17071f56",
"delegating_pk": "02d67029bb92522059225d190038230c23466e28d132d48f714f9098168a562b8a",
"receiving_pk": "03b0d0243e8954b408047eee3b09b5ed132ccc25ec70e99fc74b6e9f54e5ecf9c7",