Merge pull request #267 from fjarri/interfacing-2

Interfacing with Rust, part 2
pull/269/head
Bogdan Opanchuk 2021-05-26 23:04:58 -07:00 committed by GitHub
commit 2ad8223b68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 1353 additions and 840 deletions

View File

@ -121,9 +121,12 @@ commands:
- run:
name: Install Python dependencies with Pipenv
command: pipenv install --python << parameters.python_version >> --dev --skip-lock --pre
# Workaround for pipenv check erroneously requiring pip>=21.1,
# even if it is in fact installed and that version is fixed in Pipfile.
# See https://github.com/pypa/pipenv/issues/4147
- run:
name: Check PEP 508 Requirements
command: pipenv check
command: pipenv check --ignore 40291
- persist_to_workspace:
root: ~/.local/share/virtualenvs/
paths:

View File

@ -62,13 +62,14 @@ Additionally, users that delegate access to their data (like Alice, in this exam
.. code-block:: python
from umbral import SecretKey, PublicKey
from umbral import SecretKey, PublicKey, Signer
# Generate Umbral keys for Alice.
alices_secret_key = SecretKey.random()
alices_public_key = PublicKey.from_secret_key(alices_secret_key)
alices_signing_key = SecretKey.random()
alices_signer = Signer(alices_signing_key)
alices_verifying_key = PublicKey.from_secret_key(alices_signing_key)
# Generate Umbral keys for Bob.
@ -112,7 +113,7 @@ which are next sent to N proxies or *Ursulas*.
# In this example, 10 out of 20.
kfrags = generate_kfrags(delegating_sk=alices_secret_key,
receiving_pk=bobs_public_key,
signing_sk=alices_signing_key,
signer=alices_signer,
threshold=10,
num_kfrags=20)

View File

@ -1,6 +1,6 @@
import random
from umbral import (
SecretKey, PublicKey, GenericError,
SecretKey, PublicKey, Signer, GenericError, CapsuleFrag,
encrypt, generate_kfrags, reencrypt, decrypt_original, decrypt_reencrypted)
# Generate an Umbral key pair
@ -13,6 +13,7 @@ alices_public_key = PublicKey.from_secret_key(alices_secret_key)
alices_signing_key = SecretKey.random()
alices_verifying_key = PublicKey.from_secret_key(alices_signing_key)
alices_signer = Signer(alices_signing_key)
# Encrypt some data for Alice
# ---------------------------
@ -58,7 +59,7 @@ except GenericError:
kfrags = generate_kfrags(delegating_sk=alices_secret_key,
receiving_pk=bobs_public_key,
signing_sk=alices_signing_key,
signer=alices_signer,
threshold=10,
num_kfrags=20)
@ -85,14 +86,18 @@ assert len(cfrags) == 10
# Bob checks the capsule fragments
# --------------------------------
# Bob can verify that the capsule fragments are valid and really originate from Alice,
# If Bob received the capsule fragments in serialized form,
# he can verify that they are valid and really originate from Alice,
# using Alice's public keys.
assert all(cfrag.verify(capsule,
delegating_pk=alices_public_key,
receiving_pk=bobs_public_key,
signing_pk=alices_verifying_key)
for cfrag in cfrags)
suspicious_cfrags = [CapsuleFrag.from_bytes(bytes(cfrag)) for cfrag in cfrags]
cfrags = [cfrag.verify(capsule,
verifying_pk=alices_verifying_key,
delegating_pk=alices_public_key,
receiving_pk=bobs_public_key,
)
for cfrag in suspicious_cfrags]
# Bob opens the capsule
# ------------------------------------
@ -101,7 +106,7 @@ assert all(cfrag.verify(capsule,
bob_cleartext = decrypt_reencrypted(decrypting_sk=bobs_secret_key,
delegating_pk=alices_public_key,
capsule=bob_capsule,
cfrags=cfrags,
verified_cfrags=cfrags,
ciphertext=ciphertext)
print(bob_cleartext)
assert bob_cleartext == plaintext

View File

@ -22,7 +22,7 @@
"metadata": {},
"outputs": [],
"source": [
"from umbral import SecretKey, PublicKey\n",
"from umbral import SecretKey, PublicKey, Signer\n",
"\n",
"\n",
"# Alice's Keys\n",
@ -30,7 +30,8 @@
"alices_public_key = PublicKey.from_secret_key(alices_private_key)\n",
"\n",
"alices_signing_key = SecretKey.random()\n",
"alices_verifying_key = PublicKey.from_secret_key(alices_signing_key)"
"alices_verifying_key = PublicKey.from_secret_key(alices_signing_key)\n",
"alices_signer = Signer(alices_signing_key)"
]
},
{
@ -55,7 +56,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"b'\\x1c\\xa0\\xa83\\x0cv\\x97\\x02d\\xe9\\xe9\\xc5_\\x9d5NRGRx\\xd4\\xc9\\x17%\\x9b\\xb4\\x05\\xd1\\xc2\\x1e\\x9d\\x0b\\xbf\\xb4g\\xf0n\\xfe\\x9eM\\x93\\xe0\\xbf#l\\xf9\\x033\\xb00\\xf5\\r\\xff\\xc9\\x133C\\xf0\\xa3\\xc0\\xd1e\\xdb~.E$%'\n"
"b'\\xfb\\xc3T\\xb2\\x89=\\x08X\\xb1<\\xd0G/\\xab\\x8c\\xac\\x7f\\xd4)\\xcbB\\xcb^\\x99;P\\x9c\\xbf\\xaaf\\x03\\xdd\\n\\x1f$\\x1b\\xfb\\x88\\xfa\\xcd\\xe2\\x11\\x8d\\xcf\\xe5\\x88\\xaf\\x00\\xfe\\xcb\\x9d\\xf83\\x17\\x9b\\xdd\\xba\\xab\\x8b\\x08\\xbe\\xb1M\\x80\\xf1<S#'\n"
]
}
],
@ -169,7 +170,7 @@
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
@ -179,7 +180,7 @@
"M, N = 10, 20 # the threshold and the total number of fragments\n",
"kfrags = generate_kfrags(delegating_sk=alices_private_key,\n",
" receiving_pk=bobs_public_key,\n",
" signing_sk=alices_signing_key,\n",
" signer=alices_signer,\n",
" threshold=M,\n",
" num_kfrags=N)"
]
@ -195,7 +196,7 @@
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
@ -218,22 +219,27 @@
"metadata": {},
"source": [
"## Bob checks the capsule fragments\n",
"Bob can verify that the capsule fragments are valid and really originate from Alice, using Alice's public keys."
"If Bob received the capsule fragments in serialized form, he can verify that they are valid and really originate from Alice, using Alice's public keys."
]
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 10,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"assert all(cfrag.verify(capsule,\n",
" delegating_pk=alices_public_key,\n",
" receiving_pk=bobs_public_key,\n",
" signing_pk=alices_verifying_key)\n",
" for cfrag in cfrags)"
"from umbral import CapsuleFrag\n",
"\n",
"suspicious_cfrags = [CapsuleFrag.from_bytes(bytes(cfrag)) for cfrag in cfrags]\n",
"\n",
"cfrags = [cfrag.verify(capsule,\n",
" verifying_pk=alices_verifying_key,\n",
" delegating_pk=alices_public_key,\n",
" receiving_pk=bobs_public_key,\n",
" )\n",
" for cfrag in suspicious_cfrags]"
]
},
{
@ -263,7 +269,7 @@
"bob_cleartext = decrypt_reencrypted(decrypting_sk=bobs_private_key,\n",
" delegating_pk=alices_public_key,\n",
" capsule=capsule,\n",
" cfrags=cfrags,\n",
" verified_cfrags=cfrags,\n",
" ciphertext=ciphertext)\n",
"\n",
"print(bob_cleartext)\n",

View File

@ -19,6 +19,14 @@ Keys
:members:
:show-inheritance:
.. autoclass:: Signer
:members:
.. autoclass:: Signature()
:members:
:special-members: __eq__, __hash__
:show-inheritance:
Intermediate objects
--------------------
@ -31,11 +39,17 @@ Intermediate objects
:special-members: __eq__, __hash__
:show-inheritance:
.. autoclass:: VerifiedKeyFrag()
:special-members: __eq__, __hash__
.. autoclass:: CapsuleFrag()
:members: verify
:members:
:special-members: __eq__, __hash__
:show-inheritance:
.. autoclass:: VerifiedCapsuleFrag()
:special-members: __eq__, __hash__
Encryption, re-encryption and decryption
----------------------------------------
@ -55,6 +69,9 @@ Utilities
.. autoclass:: umbral.GenericError
:show-inheritance:
.. autoclass:: umbral.VerificationError
:show-inheritance:
.. autoclass:: umbral.serializable.Serializable
:members: from_bytes
:special-members: __bytes__

View File

@ -20,8 +20,8 @@
# -- Project information -----------------------------------------------------
project = 'pyUmbral'
copyright = u'2019, Michael Egorov, Justin Myles Holmes, David Nuñez, John Pacific, Kieran Prasch'
author = u'Michael Egorov, Justin Myles Holmes, David Nuñez, John Pacific, Kieran Prasch'
copyright = u'2019, Michael Egorov, Justin Myles Holmes, David Nuñez, John Pacific, Kieran Prasch, Bogdan Opanchuk'
author = u'Michael Egorov, Justin Myles Holmes, David Nuñez, John Pacific, Kieran Prasch, Bogdan Opanchuk'
# The short X.Y version
version = '0.1'
@ -133,7 +133,7 @@ latex_elements = {
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'pyUmbral.tex', 'pyUmbral Documentation',
u'Michael Egorov, Justin Myles Holmes, David Nuñez, John Pacific, Kieran Prasch', 'manual'),
u'Michael Egorov, Justin Myles Holmes, David Nuñez, John Pacific, Kieran Prasch, Bogdan Opanchuk', 'manual'),
]

View File

@ -43,13 +43,14 @@ A delegating key pair and a signing key pair.
.. doctest:: capsule_story
>>> from umbral import SecretKey, PublicKey
>>> from umbral import SecretKey, PublicKey, Signer
>>> alices_secret_key = SecretKey.random()
>>> alices_public_key = PublicKey.from_secret_key(alices_secret_key)
>>> alices_signing_key = SecretKey.random()
>>> alices_verifying_key = PublicKey.from_secret_key(alices_signing_key)
>>> alices_signer = Signer(alices_signing_key)
Encrypt with a public key
@ -105,7 +106,7 @@ but Bob needs to get only 10 re-encryptions to activate the capsule.
>>> from umbral import generate_kfrags
>>> kfrags = generate_kfrags(delegating_sk=alices_secret_key,
... receiving_pk=bobs_public_key,
... signing_sk=alices_signing_key,
... signer=alices_signer,
... threshold=10,
... num_kfrags=20)
@ -172,17 +173,20 @@ Decryption
Bob checks the capsule fragments
--------------------------------
Bob can verify that the capsule fragments are valid and really originate from Alice,
If Bob received the capsule fragments in serialized form,
he can verify that they are valid and really originate from Alice,
using Alice's public keys.
.. doctest:: capsule_story
>>> all(cfrag.verify(capsule,
... delegating_pk=alices_public_key,
... receiving_pk=bobs_public_key,
... signing_pk=alices_verifying_key)
... for cfrag in cfrags)
True
>>> from umbral import CapsuleFrag
>>> suspicious_cfrags = [CapsuleFrag.from_bytes(bytes(cfrag)) for cfrag in cfrags]
>>> cfrags = [cfrag.verify(capsule,
... verifying_pk=alices_verifying_key,
... delegating_pk=alices_public_key,
... receiving_pk=bobs_public_key,
... )
... for cfrag in suspicious_cfrags]
Bob opens the capsule
@ -195,7 +199,7 @@ Finally, Bob decrypts the re-encrypted ciphertext using his key.
>>> cleartext = decrypt_reencrypted(decrypting_sk=bobs_secret_key,
... delegating_pk=alices_public_key,
... capsule=capsule,
... cfrags=cfrags,
... verified_cfrags=cfrags,
... ciphertext=ciphertext)

254
pylintrc Normal file
View File

@ -0,0 +1,254 @@
[MASTER]
# Specify a configuration file.
#rcfile=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
# Profiled execution.
profile=no
# Add files or directories to the blacklist. They should be base names, not
# paths.
ignore=CVS
# Pickle collected data for later comparisons.
persistent=yes
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
[MESSAGES CONTROL]
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time.
#enable=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once).
# disable warnings:
# - C0103 (we have a lot of short names)
# - C0114 (flat package, no need for module docstrings)
# - C0116 (we have many short self-documenting functions)
disable=C0103,C0114,C0116
[REPORTS]
# Set the output format. Available formats are text, parseable, colorized, msvs
# (visual studio) and html
output-format=text
# Include message's id in output
include-ids=no
# Put messages in a separate file for each module / package specified on the
# command line instead of printing them on stdout. Reports (if any) will be
# written in a file name "pylint_global.[txt|html]".
files-output=no
# Tells whether to display a full report or only the messages
reports=yes
# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
# respectively contain the number of errors / warnings messages and the total
# number of statements analyzed. This is used by the global evaluation report
# (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Add a comment according to your evaluation note. This is used by the global
# evaluation report (RP0004).
comment=no
[BASIC]
# Required attributes for module, separated by a comma
required-attributes=
# List of builtins function names that should not be used, separated by a comma
bad-functions=map,filter,apply,input
# Regular expression which should only match correct module names
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Regular expression which should only match correct module level names
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Regular expression which should only match correct class names
class-rgx=[A-Z_][a-zA-Z0-9]+$
# Regular expression which should only match correct function names
function-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct method names
method-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct instance attribute names
attr-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct argument names
argument-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct variable names
variable-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct list comprehension /
# generator expression variable names
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
# Good variable names which should always be accepted, separated by a comma
good-names=i,j,k,ex,Run,_
# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata
# Regular expression which should only match functions or classes name which do
# not require a docstring
no-docstring-rgx=__.*__
[FORMAT]
# Maximum number of characters on a single line.
max-line-length=100
# Maximum number of lines in a module
max-module-lines=1000
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,XXX,TODO
[SIMILARITIES]
# Minimum lines number of a similarity.
min-similarity-lines=4
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
[TYPECHECK]
# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes
# List of classes names for which member attributes should not be checked
# (useful for classes with attributes dynamically set).
ignored-classes=SQLObject
# When zope mode is activated, add a predefined set of Zope acquired attributes
# to generated-members.
zope=no
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E0201 when accessed. Python regular
# expressions are accepted.
generated-members=REQUEST,acl_users,aq_parent
[VARIABLES]
# Tells whether we should check for unused import in __init__ files.
init-import=no
# A regular expression matching the beginning of the name of dummy variables
# (i.e. not used).
dummy-variables-rgx=_|dummy
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid to define new builtins when possible.
additional-builtins=
[CLASSES]
# List of interface methods to ignore, separated by a comma. This is used for
# instance to not check methods defines in Zope's Interface base class.
ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,__new__,setUp
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
[DESIGN]
# Maximum number of arguments for function / method
max-args=5
# Argument names that match this expression will be ignored. Default to name
# with leading underscore
ignored-argument-names=_.*
# Maximum number of locals for function / method body
max-locals=20
# Maximum number of return / yield for function / method body
max-returns=6
# Maximum number of branch for function / method body
max-branchs=12
# Maximum number of statements in function / method body
max-statements=50
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of attributes for a class (see R0902).
max-attributes=7
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
[IMPORTS]
# Deprecated modules which should not be used, separated by a comma
deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
import-graph=
# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled)
ext-import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled)
int-import-graph=
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=Exception

View File

@ -1,6 +1,6 @@
import pytest
from umbral import SecretKey, PublicKey, generate_kfrags, encrypt
from umbral import SecretKey, PublicKey, Signer, generate_kfrags, encrypt
@pytest.fixture
@ -17,12 +17,23 @@ def bobs_keys():
return sk, pk
@pytest.fixture
def verification_keys(alices_keys, bobs_keys):
delegating_sk, signing_sk = alices_keys
_receiving_sk, receiving_pk = bobs_keys
verifying_pk = PublicKey.from_secret_key(signing_sk)
delegating_pk = PublicKey.from_secret_key(delegating_sk)
return verifying_pk, delegating_pk, receiving_pk
@pytest.fixture
def kfrags(alices_keys, bobs_keys):
delegating_sk, signing_sk = alices_keys
receiving_sk, receiving_pk = bobs_keys
yield generate_kfrags(delegating_sk=delegating_sk,
signing_sk=signing_sk,
signer=Signer(signing_sk),
receiving_pk=receiving_pk,
threshold=6, num_kfrags=10)

View File

@ -41,6 +41,7 @@ def __standard_encryption_api(umbral) -> tuple:
delegating_pk = umbral.PublicKey.from_secret_key(delegating_sk)
signing_sk = umbral.SecretKey.random()
signer = umbral.Signer(signing_sk)
receiving_sk = umbral.SecretKey.random()
receiving_pk = umbral.PublicKey.from_secret_key(receiving_sk)
@ -48,7 +49,7 @@ def __standard_encryption_api(umbral) -> tuple:
plain_data = os.urandom(32)
capsule, ciphertext = umbral.encrypt(delegating_pk, plain_data)
return delegating_sk, receiving_pk, signing_sk, ciphertext, capsule
return delegating_sk, receiving_pk, signer, ciphertext, capsule
#
@ -64,8 +65,8 @@ def __standard_encryption_api(umbral) -> tuple:
def test_generate_kfrags_performance(benchmark, m: int, n: int, umbral) -> None:
def __setup():
delegating_sk, receiving_pk, signing_sk, ciphertext, capsule = __standard_encryption_api(umbral)
return (delegating_sk, receiving_pk, signing_sk, m, n, True, True), {}
delegating_sk, receiving_pk, signer, ciphertext, capsule = __standard_encryption_api(umbral)
return (delegating_sk, receiving_pk, signer, m, n, True, True), {}
benchmark.pedantic(umbral.generate_kfrags, setup=__setup, rounds=1000)
assert True # ensure function finishes and succeeds.
@ -84,8 +85,8 @@ def test_generate_kfrags_performance(benchmark, m: int, n: int, umbral) -> None:
def test_random_frag_reencryption_performance(benchmark, m: int, n: int, umbral) -> None:
def __setup():
delegating_sk, receiving_pk, signing_sk, ciphertext, capsule = __standard_encryption_api(umbral)
kfrags = umbral.generate_kfrags(delegating_sk, receiving_pk, signing_sk, m, n, True, True)
delegating_sk, receiving_pk, signer, ciphertext, capsule = __standard_encryption_api(umbral)
kfrags = umbral.generate_kfrags(delegating_sk, receiving_pk, signer, m, n, True, True)
one_kfrag, *remaining_kfrags = kfrags
return (capsule, one_kfrag), {}
@ -104,8 +105,8 @@ def test_random_frag_reencryption_performance(benchmark, m: int, n: int, umbral)
@pytest.mark.parametrize("m, n", ((6, 10), ))
def test_single_frag_reencryption_performance(benchmark, m: int, n: int, umbral) -> None:
delegating_sk, receiving_pk, signing_sk, ciphertext, capsule = __standard_encryption_api(umbral)
kfrags = umbral.generate_kfrags(delegating_sk, receiving_pk, signing_sk, m, n, True, True)
delegating_sk, receiving_pk, signer, ciphertext, capsule = __standard_encryption_api(umbral)
kfrags = umbral.generate_kfrags(delegating_sk, receiving_pk, signer, m, n, True, True)
one_kfrag, *remaining_kfrags = kfrags
args, kwargs = (capsule, one_kfrag), {}

View File

@ -17,6 +17,7 @@ def __produce_kfrags_and_capsule(m: int, n: int) -> Tuple[List[umbral.KeyFrag],
delegating_pk = umbral.PublicKey.from_secret_key(delegating_sk)
signing_sk = umbral.SecretKey.random()
signer = umbral.Signer(signing_sk)
receiving_sk = umbral.SecretKey.random()
receiving_pk = umbral.PublicKey.from_secret_key(receiving_sk)
@ -24,7 +25,7 @@ def __produce_kfrags_and_capsule(m: int, n: int) -> Tuple[List[umbral.KeyFrag],
plain_data = os.urandom(32)
capsule, ciphertext = umbral.encrypt(delegating_pk, plain_data)
kfrags = umbral.generate_kfrags(delegating_sk, receiving_pk, signing_sk, m, n)
kfrags = umbral.generate_kfrags(delegating_sk, receiving_pk, signer, m, n)
return kfrags, capsule

View File

@ -4,6 +4,7 @@ from umbral import (
Capsule,
SecretKey,
PublicKey,
Signer,
GenericError,
encrypt,
decrypt_original,
@ -66,17 +67,17 @@ def test_open_reencrypted(alices_keys, bobs_keys):
delegating_sk, signing_sk = alices_keys
receiving_sk, receiving_pk = bobs_keys
signing_pk = PublicKey.from_secret_key(signing_sk)
signer = Signer(signing_sk)
delegating_pk = PublicKey.from_secret_key(delegating_sk)
capsule, key = Capsule.from_public_key(delegating_pk)
kfrags = generate_kfrags(delegating_sk=delegating_sk,
signing_sk=signing_sk,
signer=signer,
receiving_pk=receiving_pk,
threshold=threshold,
num_kfrags=num_kfrags)
cfrags = [reencrypt(capsule, kfrag) for kfrag in kfrags]
cfrags = [reencrypt(capsule, kfrag).cfrag for kfrag in kfrags]
key_back = capsule.open_reencrypted(receiving_sk, delegating_pk, cfrags[:threshold])
assert key_back == key
@ -94,11 +95,11 @@ def test_open_reencrypted(alices_keys, bobs_keys):
# Mismatched cfrags
kfrags2 = generate_kfrags(delegating_sk=delegating_sk,
signing_sk=signing_sk,
signer=signer,
receiving_pk=receiving_pk,
threshold=threshold,
num_kfrags=num_kfrags)
cfrags2 = [reencrypt(capsule, kfrag) for kfrag in kfrags2]
cfrags2 = [reencrypt(capsule, kfrag).cfrag for kfrag in kfrags2]
with pytest.raises(ValueError, match="CapsuleFrags are not pairwise consistent"):
capsule.open_reencrypted(receiving_sk, delegating_pk, [cfrags2[0]] + cfrags[:threshold-1])

View File

@ -1,14 +1,12 @@
from umbral import reencrypt, CapsuleFrag, PublicKey, Capsule
import pytest
from umbral import reencrypt, CapsuleFrag, PublicKey, Capsule, VerificationError
from umbral.curve_point import CurvePoint
def test_cfrag_serialization(alices_keys, bobs_keys, capsule, kfrags):
def test_cfrag_serialization(verification_keys, capsule, kfrags):
delegating_sk, signing_sk = alices_keys
_receiving_sk, receiving_pk = bobs_keys
signing_pk = PublicKey.from_secret_key(signing_sk)
delegating_pk = PublicKey.from_secret_key(delegating_sk)
verifying_pk, delegating_pk, receiving_pk = verification_keys
metadata = b'This is an example of metadata for re-encryption request'
for kfrag in kfrags:
@ -16,56 +14,63 @@ def test_cfrag_serialization(alices_keys, bobs_keys, capsule, kfrags):
cfrag_bytes = bytes(cfrag)
new_cfrag = CapsuleFrag.from_bytes(cfrag_bytes)
assert new_cfrag == cfrag
assert new_cfrag.verify(capsule,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
signing_pk=signing_pk,
metadata=metadata)
verified_cfrag = new_cfrag.verify(capsule,
verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
metadata=metadata,
)
assert verified_cfrag == cfrag
# No metadata
assert not new_cfrag.verify(capsule,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
signing_pk=signing_pk)
with pytest.raises(VerificationError):
new_cfrag.verify(capsule,
verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
)
# Wrong metadata
assert not new_cfrag.verify(capsule,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
signing_pk=signing_pk,
metadata=b'Not the same 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
assert not new_cfrag.verify(capsule,
delegating_pk=receiving_pk,
receiving_pk=receiving_pk,
signing_pk=signing_pk,
metadata=metadata)
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
assert not new_cfrag.verify(capsule,
delegating_pk=delegating_pk,
receiving_pk=delegating_pk,
signing_pk=signing_pk,
metadata=metadata)
with pytest.raises(VerificationError):
new_cfrag.verify(capsule,
verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=delegating_pk,
metadata=metadata,
)
# Wrong signing key
assert not new_cfrag.verify(capsule,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
signing_pk=receiving_pk,
metadata=metadata)
with pytest.raises(VerificationError):
new_cfrag.verify(capsule,
verifying_pk=receiving_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
metadata=metadata,
)
def test_cfrag_serialization_no_metadata(alices_keys, bobs_keys, capsule, kfrags):
def test_cfrag_serialization_no_metadata(verification_keys, capsule, kfrags):
delegating_sk, signing_sk = alices_keys
_receiving_sk, receiving_pk = bobs_keys
signing_pk = PublicKey.from_secret_key(signing_sk)
delegating_pk = PublicKey.from_secret_key(delegating_sk)
verifying_pk, delegating_pk, receiving_pk = verification_keys
for kfrag in kfrags:
@ -74,65 +79,68 @@ def test_cfrag_serialization_no_metadata(alices_keys, bobs_keys, capsule, kfrags
cfrag_bytes = bytes(cfrag)
new_cfrag = CapsuleFrag.from_bytes(cfrag_bytes)
assert new_cfrag.verify(capsule,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
signing_pk=signing_pk)
verified_cfrag = new_cfrag.verify(capsule,
verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
)
assert verified_cfrag == cfrag
assert not new_cfrag.verify(capsule,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
signing_pk=signing_pk,
metadata=b'some metadata')
with pytest.raises(VerificationError):
new_cfrag.verify(capsule,
verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
metadata=b'some metadata',
)
def test_cfrag_with_wrong_capsule(alices_keys, bobs_keys,
kfrags, capsule_and_ciphertext, message):
def test_cfrag_with_wrong_capsule(verification_keys, kfrags, capsule_and_ciphertext, message):
capsule, ciphertext = capsule_and_ciphertext
delegating_sk, signing_sk = alices_keys
delegating_pk = PublicKey.from_secret_key(delegating_sk)
_receiving_sk, receiving_pk = bobs_keys
verifying_pk, delegating_pk, receiving_pk = verification_keys
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 = CapsuleFrag.from_bytes(bytes(cfrag)) # de-verify
assert not cfrag.verify(capsule_alice1,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
signing_pk=PublicKey.from_secret_key(signing_sk),
metadata=metadata)
with pytest.raises(VerificationError):
cfrag.verify(capsule_alice1,
verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
metadata=metadata,
)
def test_cfrag_with_wrong_data(kfrags, alices_keys, bobs_keys, capsule_and_ciphertext, message):
def test_cfrag_with_wrong_data(verification_keys, kfrags, capsule_and_ciphertext, message):
capsule, ciphertext = capsule_and_ciphertext
delegating_sk, signing_sk = alices_keys
delegating_pk = PublicKey.from_secret_key(delegating_sk)
_receiving_sk, receiving_pk = bobs_keys
verifying_pk, delegating_pk, receiving_pk = verification_keys
metadata = b"some metadata"
cfrag = reencrypt(capsule, kfrags[0], metadata=metadata)
# Let's put random garbage in one of the cfrags
cfrag = CapsuleFrag.from_bytes(bytes(cfrag)) # de-verify
cfrag.point_e1 = CurvePoint.random()
cfrag.point_v1 = CurvePoint.random()
assert not cfrag.verify(capsule,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
signing_pk=PublicKey.from_secret_key(signing_sk),
metadata=metadata)
with pytest.raises(VerificationError):
cfrag.verify(capsule,
verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
metadata=metadata,
)
def test_cfrag_is_hashable(capsule, kfrags):
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')
@ -140,10 +148,22 @@ def test_cfrag_is_hashable(capsule, kfrags):
assert hash(cfrag0) != hash(cfrag1)
new_cfrag = CapsuleFrag.from_bytes(bytes(cfrag0))
assert hash(new_cfrag) == hash(cfrag0)
assert hash(new_cfrag) != hash(cfrag0)
verified_cfrag = new_cfrag.verify(capsule,
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')
s = str(cfrag0)
assert 'CapsuleFrag' in s
assert 'VerifiedCapsuleFrag' in s
s = str(CapsuleFrag.from_bytes(bytes(cfrag0)))
assert "VerifiedCapsuleFrag" not in s
assert "CapsuleFrag" in s

View File

@ -93,30 +93,32 @@ def test_encrypt_decrypt(implementations):
def _generate_kfrags(umbral, delegating_sk_bytes, receiving_pk_bytes,
signing_sk_bytes, threshold, num_frags):
signing_sk_bytes, threshold, num_kfrags):
delegating_sk = umbral.SecretKey.from_bytes(delegating_sk_bytes)
receiving_pk = umbral.PublicKey.from_bytes(receiving_pk_bytes)
signing_sk = umbral.SecretKey.from_bytes(signing_sk_bytes)
kfrags = umbral.generate_kfrags(delegating_sk,
receiving_pk,
signing_sk,
threshold,
num_frags,
True,
True,
kfrags = umbral.generate_kfrags(delegating_sk=delegating_sk,
receiving_pk=receiving_pk,
signer=umbral.Signer(signing_sk),
threshold=threshold,
num_kfrags=num_kfrags,
sign_delegating_key=True,
sign_receiving_key=True,
)
return [bytes(kfrag) for kfrag in kfrags]
def _verify_kfrags(umbral, kfrags_bytes, signing_pk_bytes, delegating_pk_bytes, receiving_pk_bytes):
def _verify_kfrags(umbral, kfrags_bytes, verifying_pk_bytes, delegating_pk_bytes, receiving_pk_bytes):
kfrags = [umbral.KeyFrag.from_bytes(kfrag_bytes) for kfrag_bytes in kfrags_bytes]
signing_pk = umbral.PublicKey.from_bytes(signing_pk_bytes)
verifying_pk = umbral.PublicKey.from_bytes(verifying_pk_bytes)
delegating_pk = umbral.PublicKey.from_bytes(delegating_pk_bytes)
receiving_pk = umbral.PublicKey.from_bytes(receiving_pk_bytes)
assert all(kfrag.verify(signing_pk, delegating_pk, receiving_pk) for kfrag in kfrags)
return [kfrag.verify(verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk) for kfrag in kfrags]
def test_kfrags(implementations):
@ -124,49 +126,55 @@ def test_kfrags(implementations):
umbral1, umbral2 = implementations
threshold = 2
num_frags = 3
num_kfrags = 3
plaintext = b'peace at dawn'
# On client 1
receiving_sk_bytes, receiving_pk_bytes = _create_keypair(umbral1)
delegating_sk_bytes, delegating_pk_bytes = _create_keypair(umbral1)
signing_sk_bytes, signing_pk_bytes = _create_keypair(umbral1)
signing_sk_bytes, verifying_pk_bytes = _create_keypair(umbral1)
kfrags_bytes = _generate_kfrags(umbral1, delegating_sk_bytes, receiving_pk_bytes,
signing_sk_bytes, threshold, num_frags)
signing_sk_bytes, threshold, num_kfrags)
# On client 2
_verify_kfrags(umbral2, kfrags_bytes, signing_pk_bytes, delegating_pk_bytes, receiving_pk_bytes)
_verify_kfrags(umbral2, kfrags_bytes, verifying_pk_bytes, delegating_pk_bytes, receiving_pk_bytes)
def _reencrypt(umbral, capsule_bytes, kfrags_bytes, threshold, metadata):
def _reencrypt(umbral, verifying_pk_bytes, delegating_pk_bytes, receiving_pk_bytes,
capsule_bytes, kfrags_bytes, threshold, metadata):
capsule = umbral.Capsule.from_bytes(bytes(capsule_bytes))
kfrags = [umbral.KeyFrag.from_bytes(kfrag_bytes) for kfrag_bytes in kfrags_bytes]
cfrags = [umbral.reencrypt(capsule, kfrag, metadata=metadata) for kfrag in kfrags[:threshold]]
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]]
return [bytes(cfrag) for cfrag in cfrags]
def _decrypt_reencrypted(umbral, receiving_sk_bytes, delegating_pk_bytes, signing_pk_bytes,
def _decrypt_reencrypted(umbral, receiving_sk_bytes, delegating_pk_bytes, verifying_pk_bytes,
capsule_bytes, cfrags_bytes, ciphertext, metadata):
receiving_sk = umbral.SecretKey.from_bytes(receiving_sk_bytes)
receiving_pk = umbral.PublicKey.from_secret_key(receiving_sk)
delegating_pk = umbral.PublicKey.from_bytes(delegating_pk_bytes)
signing_pk = umbral.PublicKey.from_bytes(signing_pk_bytes)
verifying_pk = umbral.PublicKey.from_bytes(verifying_pk_bytes)
capsule = umbral.Capsule.from_bytes(bytes(capsule_bytes))
cfrags = [umbral.CapsuleFrag.from_bytes(cfrag_bytes) for cfrag_bytes in cfrags_bytes]
assert all(cfrag.verify(capsule, delegating_pk, receiving_pk, signing_pk, metadata=metadata)
for cfrag in cfrags)
verified_cfrags = [cfrag.verify(capsule,
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(receiving_sk,
delegating_pk,
capsule,
cfrags,
ciphertext,
plaintext = umbral.decrypt_reencrypted(decrypting_sk=receiving_sk,
delegating_pk=delegating_pk,
capsule=capsule,
verified_cfrags=verified_cfrags,
ciphertext=ciphertext,
)
return plaintext
@ -178,28 +186,60 @@ def test_reencrypt(implementations):
metadata = b'metadata'
threshold = 2
num_frags = 3
num_kfrags = 3
plaintext = b'peace at dawn'
# On client 1
receiving_sk_bytes, receiving_pk_bytes = _create_keypair(umbral1)
delegating_sk_bytes, delegating_pk_bytes = _create_keypair(umbral1)
signing_sk_bytes, signing_pk_bytes = _create_keypair(umbral1)
signing_sk_bytes, verifying_pk_bytes = _create_keypair(umbral1)
capsule_bytes, ciphertext = _encrypt(umbral1, plaintext, delegating_pk_bytes)
kfrags_bytes = _generate_kfrags(umbral1, delegating_sk_bytes, receiving_pk_bytes,
signing_sk_bytes, threshold, num_frags)
signing_sk_bytes, threshold, num_kfrags)
# On client 2
cfrags_bytes = _reencrypt(umbral2, capsule_bytes, kfrags_bytes, threshold, metadata)
cfrags_bytes = _reencrypt(umbral2, verifying_pk_bytes, delegating_pk_bytes, receiving_pk_bytes,
capsule_bytes, kfrags_bytes, threshold, metadata)
# On client 1
plaintext_reencrypted = _decrypt_reencrypted(umbral1,
receiving_sk_bytes, delegating_pk_bytes, signing_pk_bytes,
receiving_sk_bytes, delegating_pk_bytes, verifying_pk_bytes,
capsule_bytes, cfrags_bytes, ciphertext, metadata)
assert plaintext_reencrypted == plaintext
def _sign_message(umbral, sk_bytes, message):
sk = umbral.SecretKey.from_bytes(sk_bytes)
signer = umbral.Signer(sk)
assert signer.verifying_key() == umbral.PublicKey.from_secret_key(sk)
return bytes(signer.sign(message))
def _verify_message(umbral, pk_bytes, signature_bytes, message):
pk = umbral.PublicKey.from_bytes(pk_bytes)
signature = umbral.Signature.from_bytes(signature_bytes)
return signature.verify(pk, message)
def test_signer(implementations):
umbral1, umbral2 = implementations
message = b'peace at dawn'
sk_bytes, pk_bytes = _create_keypair(umbral1)
signature1_bytes = _sign_message(umbral1, sk_bytes, message)
signature2_bytes = _sign_message(umbral2, sk_bytes, message)
# Signatures are random, so we can't compare them.
# Cross-verify instead
assert _verify_message(umbral1, pk_bytes, signature2_bytes, message)
assert _verify_message(umbral2, pk_bytes, signature1_bytes, message)

View File

@ -1,19 +1,17 @@
import pytest
from umbral.openssl import Curve, bn_to_int, point_to_affine_coords
from umbral.curve import CURVE, CURVES, SECP256R1, SECP256K1, SECP384R1
from umbral.curve import CURVE, SECP256K1
def test_supported_curves():
# Ensure we have the correct number of supported curves hardcoded
number_of_supported_curves = 3
number_of_supported_curves = 1
assert len(Curve._supported_curves) == number_of_supported_curves
# Manually ensure the `_supported curves` dict contains only valid supported curves
assert Curve._supported_curves[415] == 'secp256r1'
assert Curve._supported_curves[714] == 'secp256k1'
assert Curve._supported_curves[715] == 'secp384r1'
def test_create_by_nid():
@ -46,20 +44,12 @@ def test_create_by_name():
def test_curve_constants():
test_p256 = SECP256R1
test_secp256k1 = SECP256K1
test_p384 = SECP384R1
assert CURVE == SECP256K1
# Test the hardcoded curve NIDs are correct:
assert test_p256.nid == 415
assert test_secp256k1.nid == 714
assert test_p384.nid == 715
# Ensure every curve constant is in the CURVES collection
number_of_supported_curves = 3
assert len(CURVES) == number_of_supported_curves
# Ensure all supported curves can be initialized
for nid, name in Curve._supported_curves.items():
@ -69,7 +59,8 @@ def test_curve_constants():
def test_curve_str():
for curve in CURVES:
for nid in Curve._supported_curves:
curve = Curve(nid=nid)
s = str(curve)
assert str(curve.nid) in s
assert str(curve.name) in s
@ -91,23 +82,3 @@ def test_secp256k1():
assert info['generator'] == (
0x79BE667E_F9DCBBAC_55A06295_CE870B07_029BFCDB_2DCE28D9_59F2815B_16F81798,
0x483ADA77_26A3C465_5DA4FBFC_0E1108A8_FD17B448_A6855419_9C47D08F_FB10D4B8)
def test_p256():
info = _curve_info(SECP256R1)
assert info['order'] == 0xFFFFFFFF_00000000_FFFFFFFF_FFFFFFFF_BCE6FAAD_A7179E84_F3B9CAC2_FC632551
assert info['field_element_size'] == 32
assert info['scalar_size'] == 32
assert info['generator'] == (
0x6B17D1F2_E12C4247_F8BCE6E5_63A440F2_77037D81_2DEB33A0_F4A13945_D898C296,
0x4FE342E2_FE1A7F9B_8EE7EB4A_7C0F9E16_2BCE3357_6B315ECE_CBB64068_37BF51F5)
def test_p384():
info = _curve_info(SECP384R1)
assert info['order'] == 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_C7634D81_F4372DDF_581A0DB2_48B0A77A_ECEC196A_CCC52973
assert info['field_element_size'] == 48
assert info['scalar_size'] == 48
assert info['generator'] == (
0xAA87CA22_BE8B0537_8EB1C71E_F320AD74_6E1D3B62_8BA79B98_59F741E0_82542A38_5502F25D_BF55296C_3A545E38_72760AB7,
0x3617DE4A_96262C6F_5D9E98BF_9292DC29_F8F41DBD_289A147C_E9DA3113_B5F0B8C0_0A60B1CE_1D7E819D_7A431D7C_90EA0E5F)

View File

@ -24,16 +24,6 @@ def test_generator_point():
assert g1 == g2
def test_to_and_from_affine():
x = 17004608369308732328368332205668001941491834793934321461466076545247324070015
y = 69725941631324401609944843130171147910924748427773762412028916504484868631573
p = CurvePoint.from_affine(x, y)
assert p.to_affine() == (x, y)
def test_invalid_serialized_points():
field_order = 2**256 - 0x1000003D1
@ -72,19 +62,15 @@ def test_serialize_point_at_infinity():
assert bytes_point_at_infinity == b'\x00'
def test_coords_with_special_characteristics():
def test_to_affine():
p = CurvePoint.generator()
x_ref = 0x79BE667E_F9DCBBAC_55A06295_CE870B07_029BFCDB_2DCE28D9_59F2815B_16F81798
y_ref = 0x483ADA77_26A3C465_5DA4FBFC_0E1108A8_FD17B448_A6855419_9C47D08F_FB10D4B8
assert p.to_affine() == (x_ref, y_ref)
# Testing that a point with x coordinate greater than the curve order is still valid.
# In particular, we will test the last valid point from the default curve (secp256k1)
# whose x coordinate is `field_order - 3` and is greater than the order of the curve
field_order = 2**256 - 0x1000003D1
compressed = b'\x02' + (field_order-3).to_bytes(32, 'big')
last_point = CurvePoint.from_bytes(compressed)
# The same point, but obtained through the from_affine method
x = 115792089237316195423570985008687907853269984665640564039457584007908834671660
y = 109188863561374057667848968960504138135859662956057034999983532397866404169138
assert last_point == CurvePoint.from_affine(x, y)
def test_identity_to_affine():
p = CurvePoint.generator()
identity = p - p
with pytest.raises(ValueError):
identity.to_affine()

View File

@ -1,54 +1,48 @@
import pytest
from umbral import KeyFrag, PublicKey, generate_kfrags
from umbral.key_frag import KeyFragID
from umbral import KeyFrag, PublicKey, Signer, VerificationError
from umbral.key_frag import KeyFragID, KeyFragBase
from umbral.curve_scalar import CurveScalar
def test_kfrag_serialization(alices_keys, bobs_keys, kfrags):
def test_kfrag_serialization(verification_keys, kfrags):
delegating_sk, signing_sk = alices_keys
_receiving_sk, receiving_pk = bobs_keys
signing_pk = PublicKey.from_secret_key(signing_sk)
delegating_pk = PublicKey.from_secret_key(delegating_sk)
verifying_pk, delegating_pk, receiving_pk = verification_keys
for kfrag in kfrags:
kfrag_bytes = bytes(kfrag)
new_kfrag = KeyFrag.from_bytes(kfrag_bytes)
assert new_kfrag.verify(signing_pk=signing_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk)
new_kfrag = new_kfrag.verify(verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk)
assert new_kfrag == kfrag
def test_kfrag_verification(alices_keys, bobs_keys, kfrags):
def test_kfrag_verification(verification_keys, kfrags):
delegating_sk, signing_sk = alices_keys
_receiving_sk, receiving_pk = bobs_keys
signing_pk = PublicKey.from_secret_key(signing_sk)
delegating_pk = PublicKey.from_secret_key(delegating_sk)
verifying_pk, delegating_pk, receiving_pk = verification_keys
# Wrong signature
kfrag = kfrags[0]
kfrag.id = KeyFragID.random()
kfrag.kfrag.id = KeyFragID.random()
kfrag_bytes = bytes(kfrag)
new_kfrag = KeyFrag.from_bytes(kfrag_bytes)
assert not new_kfrag.verify(signing_pk=signing_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk)
with pytest.raises(VerificationError):
new_kfrag.verify(verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk)
# Wrong key
kfrag = kfrags[1]
kfrag.key = CurveScalar.random_nonzero()
kfrag.kfrag.key = CurveScalar.random_nonzero()
kfrag_bytes = bytes(kfrag)
new_kfrag = KeyFrag.from_bytes(kfrag_bytes)
assert not new_kfrag.verify(signing_pk=signing_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk)
with pytest.raises(VerificationError):
new_kfrag.verify(verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk)
@pytest.mark.parametrize('sign_delegating_key',
@ -62,18 +56,17 @@ def test_kfrag_signing(alices_keys, bobs_keys, sign_delegating_key, sign_receivi
delegating_sk, signing_sk = alices_keys
_receiving_sk, receiving_pk = bobs_keys
signing_pk = PublicKey.from_secret_key(signing_sk)
verifying_pk = PublicKey.from_secret_key(signing_sk)
delegating_pk = PublicKey.from_secret_key(delegating_sk)
kfrags = generate_kfrags(delegating_sk=delegating_sk,
signing_sk=signing_sk,
receiving_pk=receiving_pk,
threshold=6,
num_kfrags=10,
sign_delegating_key=sign_delegating_key,
sign_receiving_key=sign_receiving_key)
base = KeyFragBase(delegating_sk=delegating_sk,
receiving_pk=receiving_pk,
signer=Signer(signing_sk),
threshold=6)
kfrag = kfrags[0]
kfrag = KeyFrag.from_base(base=base,
sign_delegating_key=sign_delegating_key,
sign_receiving_key=sign_receiving_key)
# serialize/deserialize to make sure sign_* fields are serialized correctly
kfrag = KeyFrag.from_bytes(bytes(kfrag))
@ -84,43 +77,50 @@ def test_kfrag_signing(alices_keys, bobs_keys, sign_delegating_key, sign_receivi
receiving_key_ok = (not sign_receiving_key) or pass_receiving_key
should_verify = delegating_key_ok and receiving_key_ok
result = kfrag.verify(signing_pk=signing_pk,
delegating_pk=delegating_pk if pass_delegating_key else None,
receiving_pk=receiving_pk if pass_receiving_key else None)
verification_passed = True
try:
kfrag.verify(verifying_pk=verifying_pk,
delegating_pk=delegating_pk if pass_delegating_key else None,
receiving_pk=receiving_pk if pass_receiving_key else None)
except VerificationError:
verification_passed = False
assert result == should_verify
assert verification_passed == should_verify
def test_kfrag_is_hashable(kfrags):
assert hash(kfrags[0]) != hash(kfrags[1])
new_kfrag = KeyFrag.from_bytes(bytes(kfrags[0]))
assert hash(new_kfrag) == hash(kfrags[0])
def test_kfrag_str(kfrags):
s = str(kfrags[0])
assert "KeyFrag" in s
WRONG_PARAMETERS = (
# (num_kfrags, threshold)
(-1, -1), (-1, 0), (-1, 5),
(0, -1), (0, 0), (0, 5),
(1, -1), (1, 0), (1, 5),
(5, -1), (5, 0), (5, 10)
)
@pytest.mark.parametrize("num_kfrags, threshold", WRONG_PARAMETERS)
def test_wrong_threshold_and_num_kfrags(num_kfrags, threshold, alices_keys, bobs_keys):
def test_wrong_threshold(alices_keys, bobs_keys):
delegating_sk, signing_sk = alices_keys
_receiving_sk, receiving_pk = bobs_keys
with pytest.raises(ValueError):
generate_kfrags(delegating_sk=delegating_sk,
signing_sk=signing_sk,
receiving_pk=receiving_pk,
threshold=threshold,
num_kfrags=num_kfrags)
KeyFragBase(delegating_sk=delegating_sk,
receiving_pk=receiving_pk,
signer=Signer(signing_sk),
threshold=0)
def test_kfrag_is_hashable(verification_keys, kfrags):
verifying_pk, delegating_pk, receiving_pk = verification_keys
assert hash(kfrags[0]) != hash(kfrags[1])
new_kfrag = KeyFrag.from_bytes(bytes(kfrags[0]))
# Not verified yet
assert hash(new_kfrag) != hash(kfrags[0])
verified_kfrag = new_kfrag.verify(verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk)
assert hash(verified_kfrag) == hash(kfrags[0])
def test_kfrag_str(kfrags):
s = str(kfrags[0])
assert "VerifiedKeyFrag" in s
s = str(KeyFrag.from_bytes(bytes(kfrags[0])))
assert "VerifiedKeyFrag" not in s
assert "KeyFrag" in s

View File

@ -3,8 +3,7 @@ import string
import pytest
from umbral.keys import PublicKey, SecretKey, SecretKeyFactory, Signature
from umbral.hashing import Hash
from umbral.keys import PublicKey, SecretKey, SecretKeyFactory
def test_gen_key():
@ -18,6 +17,11 @@ def test_gen_key():
assert pk == pk2
def test_secret_scalar():
sk = SecretKey.random()
assert sk.secret_scalar() == sk._scalar_key
def test_derive_key_from_label():
factory = SecretKeyFactory.random()
@ -58,7 +62,7 @@ def test_secret_key_str():
def test_secret_key_hash():
sk = SecretKey.random()
# Insecure Python hash, shouldn't be available.
with pytest.raises(NotImplementedError):
with pytest.raises(RuntimeError):
hash(sk)
@ -71,7 +75,7 @@ def test_secret_key_factory_str():
def test_secret_key_factory_hash():
skf = SecretKeyFactory.random()
# Insecure Python hash, shouldn't be available.
with pytest.raises(NotImplementedError):
with pytest.raises(RuntimeError):
hash(skf)
@ -95,7 +99,7 @@ def test_public_key_str():
assert 'PublicKey' in s
def test_keying_material_serialization():
def test_secret_key_factory_serialization():
factory = SecretKeyFactory.random()
encoded_factory = bytes(factory)
@ -117,87 +121,3 @@ def test_public_key_is_hashable():
pk3 = PublicKey.from_bytes(bytes(pk))
assert hash(pk) == hash(pk3)
@pytest.mark.parametrize('execution_number', range(20)) # Run this test 20 times.
def test_sign_and_verify(execution_number):
sk = SecretKey.random()
pk = PublicKey.from_secret_key(sk)
message = b"peace at dawn"
dst = b"dst"
digest = Hash(dst)
digest.update(message)
signature = sk.sign_digest(digest)
digest = Hash(dst)
digest.update(message)
assert signature.verify_digest(pk, digest)
@pytest.mark.parametrize('execution_number', range(20)) # Run this test 20 times.
def test_sign_serialize_and_verify(execution_number):
sk = SecretKey.random()
pk = PublicKey.from_secret_key(sk)
message = b"peace at dawn"
dst = b"dst"
digest = Hash(dst)
digest.update(message)
signature = sk.sign_digest(digest)
signature_bytes = bytes(signature)
signature_restored = Signature.from_bytes(signature_bytes)
digest = Hash(dst)
digest.update(message)
assert signature_restored.verify_digest(pk, digest)
def test_verification_fail():
sk = SecretKey.random()
pk = PublicKey.from_secret_key(sk)
message = b"peace at dawn"
dst = b"dst"
digest = Hash(dst)
digest.update(message)
signature = sk.sign_digest(digest)
# wrong DST
digest = Hash(b"other dst")
digest.update(message)
assert not signature.verify_digest(pk, digest)
# wrong message
digest = Hash(dst)
digest.update(b"no peace at dawn")
assert not signature.verify_digest(pk, digest)
# bad signature
signature_bytes = bytes(signature)
signature_bytes = b'\x00' + signature_bytes[1:]
signature_restored = Signature.from_bytes(signature_bytes)
digest = Hash(dst)
digest.update(message)
assert not signature_restored.verify_digest(pk, digest)
def test_signature_repr():
sk = SecretKey.random()
pk = PublicKey.from_secret_key(sk)
message = b"peace at dawn"
dst = b"dst"
digest = Hash(dst)
digest.update(message)
signature = sk.sign_digest(digest)
s = repr(signature)
assert 'Signature' in s

View File

@ -3,7 +3,10 @@ import pytest
from umbral import (
SecretKey,
PublicKey,
Signer,
GenericError,
KeyFrag,
CapsuleFrag,
encrypt,
generate_kfrags,
decrypt_original,
@ -52,7 +55,8 @@ def test_simple_api(num_kfrags, threshold):
delegating_pk = PublicKey.from_secret_key(delegating_sk)
signing_sk = SecretKey.random()
signing_pk = PublicKey.from_secret_key(signing_sk)
signer = Signer(signing_sk)
verifying_pk = PublicKey.from_secret_key(signing_sk)
# Key Generation (Bob)
receiving_sk = SecretKey.random()
@ -67,29 +71,56 @@ def test_simple_api(num_kfrags, threshold):
assert plaintext_decrypted == plaintext
# Split Re-Encryption Key Generation (aka Delegation)
kfrags = generate_kfrags(delegating_sk, receiving_pk, signing_sk, threshold, num_kfrags)
kfrags = generate_kfrags(delegating_sk=delegating_sk,
receiving_pk=receiving_pk,
signer=signer,
threshold=threshold,
num_kfrags=num_kfrags)
# Bob requests re-encryption to some set of M ursulas
cfrags = list()
for kfrag in kfrags[:threshold]:
# Ursula checks that the received kfrag is valid
assert kfrag.verify(signing_pk, delegating_pk, receiving_pk)
# Re-encryption by an Ursula
cfrag = reencrypt(capsule, kfrag)
# Bob collects the result
cfrags.append(cfrag)
# Bob checks that the received cfrags are valid
assert all(cfrag.verify(capsule, delegating_pk, receiving_pk, signing_pk) for cfrag in cfrags)
cfrags = [reencrypt(capsule, kfrag) for kfrag in kfrags]
# Decryption by Bob
plaintext_reenc = decrypt_reencrypted(receiving_sk,
delegating_pk,
capsule,
cfrags[:threshold],
ciphertext,
plaintext_reenc = decrypt_reencrypted(decrypting_sk=receiving_sk,
delegating_pk=delegating_pk,
capsule=capsule,
verified_cfrags=cfrags[:threshold],
ciphertext=ciphertext,
)
assert plaintext_reenc == plaintext
def test_reencrypt_unverified_kfrag(capsule, kfrags):
kfrag = KeyFrag.from_bytes(bytes(kfrags[0]))
with pytest.raises(TypeError):
reencrypt(capsule, kfrag)
def test_decrypt_unverified_cfrag(verification_keys, bobs_keys, capsule_and_ciphertext, kfrags):
verifying_pk, delegating_pk, receiving_pk = verification_keys
receiving_sk, _receiving_pk = bobs_keys
capsule, ciphertext = capsule_and_ciphertext
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,
delegating_pk=delegating_pk,
capsule=capsule,
verified_cfrags=cfrags,
ciphertext=ciphertext,
)
def test_wrong_num_kfrags(alices_keys, bobs_keys):
delegating_sk, signing_sk = alices_keys
_receiving_sk, receiving_pk = bobs_keys
# Trying to create less kfrags than the threshold
with pytest.raises(ValueError):
generate_kfrags(delegating_sk=delegating_sk,
signer=Signer(signing_sk),
receiving_pk=receiving_pk,
threshold=3,
num_kfrags=2)

111
tests/test_signing.py Normal file
View File

@ -0,0 +1,111 @@
import pytest
from umbral.keys import PublicKey, SecretKey
from umbral.signing import Signature, Signer
from umbral.hashing import Hash
@pytest.mark.parametrize('execution_number', range(20)) # Run this test 20 times.
def test_sign_and_verify(execution_number):
sk = SecretKey.random()
pk = PublicKey.from_secret_key(sk)
signer = Signer(sk)
message = b"peace at dawn" + str(execution_number).encode()
signature = signer.sign(message)
assert signature.verify(pk, message)
@pytest.mark.parametrize('execution_number', range(20)) # Run this test 20 times.
def test_sign_serialize_and_verify(execution_number):
sk = SecretKey.random()
pk = PublicKey.from_secret_key(sk)
signer = Signer(sk)
message = b"peace at dawn" + str(execution_number).encode()
signature = signer.sign(message)
signature_bytes = bytes(signature)
signature_restored = Signature.from_bytes(signature_bytes)
assert signature_restored.verify(pk, message)
def test_verification_fail():
sk = SecretKey.random()
pk = PublicKey.from_secret_key(sk)
signer = Signer(sk)
message = b"peace at dawn"
signature = signer.sign(message)
# wrong message
wrong_message = b"no peace at dawn"
assert not signature.verify(pk, wrong_message)
# bad signature
signature_bytes = bytes(signature)
signature_bytes = b'\x00' + signature_bytes[1:]
signature_restored = Signature.from_bytes(signature_bytes)
assert not signature_restored.verify(pk, message)
def test_signature_str():
sk = SecretKey.random()
pk = PublicKey.from_secret_key(sk)
signer = Signer(sk)
signature = signer.sign(b'peace at dawn')
s = str(signature)
assert 'Signature' in s
def test_signature_is_hashable():
sk = SecretKey.random()
pk = PublicKey.from_secret_key(sk)
signer = Signer(sk)
message = b'peace at dawn'
message2 = b'no peace at dawn'
signature = signer.sign(message)
signature2 = signer.sign(message2)
assert hash(signature) != hash(signature2)
signature_restored = Signature.from_bytes(bytes(signature))
assert signature == signature_restored
assert hash(signature) == hash(signature_restored)
# Different hash, since signing involves some randomness
signature3 = signer.sign(message)
assert hash(signature) != hash(signature3)
def test_signer_str():
signer = Signer(SecretKey.random())
s = str(signer)
assert s == "Signer:..."
def test_signer_hash():
signer = Signer(SecretKey.random())
# Insecure Python hash, shouldn't be available.
with pytest.raises(RuntimeError):
hash(signer)
def test_signer_bytes():
signer = Signer(SecretKey.random())
# Shouldn't be able to serialize.
with pytest.raises(RuntimeError):
bytes(signer)
def test_signer_pubkey():
sk = SecretKey.random()
pk = PublicKey.from_secret_key(sk)
signer = Signer(sk)
assert signer.verifying_key() == pk

View File

@ -122,7 +122,7 @@ def test_kfrags():
for json_kfrag in vector_suite['vectors']:
kfrag = KeyFrag.from_bytes(bytes.fromhex(json_kfrag['kfrag']))
assert kfrag.verify(signing_pk=verifying_pk,
assert kfrag.verify(verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk), \
'Invalid KeyFrag {}'.format(bytes(kfrag).hex())
@ -150,18 +150,17 @@ def test_cfrags():
metadata = bytes.fromhex(vector_suite['metadata'])
for kfrag, cfrag in kfrags_n_cfrags:
assert kfrag.verify(signing_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk), \
'Invalid KeyFrag {}'.format(bytes(kfrag.to_bytes).hex())
verified_kfrag = kfrag.verify(verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk)
new_cfrag = reencrypt(capsule, kfrag, metadata=metadata)
new_cfrag = reencrypt(capsule, verified_kfrag, metadata=metadata).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
assert new_cfrag.precursor == cfrag.precursor
assert new_cfrag.verify(capsule,
signing_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
metadata=metadata)
new_cfrag.verify(capsule,
verifying_pk=verifying_pk,
delegating_pk=delegating_pk,
receiving_pk=receiving_pk,
metadata=metadata)

View File

@ -1,7 +1,8 @@
from __future__ import absolute_import, division, print_function
__all__ = [
"__title__", "__summary__", "__version__", "__author__", "__email__", "__license__", "__copyright__", "__url__"
"__title__", "__summary__", "__version__", "__author__",
"__email__", "__license__", "__copyright__", "__url__"
]
__title__ = "umbral"

View File

@ -3,28 +3,26 @@ from .__about__ import (
)
from .capsule import Capsule
from .capsule_frag import CapsuleFrag
from .errors import GenericError
from .key_frag import KeyFrag, generate_kfrags
from .capsule_frag import CapsuleFrag, VerifiedCapsuleFrag
from .errors import GenericError, VerificationError
from .key_frag import KeyFrag, VerifiedKeyFrag
from .keys import SecretKey, PublicKey, SecretKeyFactory
from .pre import encrypt, decrypt_original, decrypt_reencrypted, reencrypt
from .pre import encrypt, decrypt_original, decrypt_reencrypted, reencrypt, generate_kfrags
from .signing import Signature, Signer
__all__ = [
"__title__",
"__summary__",
"__version__",
"__author__",
"__license__",
"__copyright__",
"__email__",
"__url__",
"SecretKey",
"PublicKey",
"SecretKeyFactory",
"Signature",
"Signer",
"Capsule",
"KeyFrag",
"VerifiedKeyFrag",
"CapsuleFrag",
"VerifiedCapsuleFrag",
"GenericError",
"VerificationError",
"encrypt",
"decrypt_original",
"generate_kfrags",

View File

@ -5,7 +5,6 @@ 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 .params import PARAMETERS
from .serializable import Serializable
if TYPE_CHECKING: # pragma: no cover
from .capsule_frag import CapsuleFrag
@ -13,10 +12,10 @@ if TYPE_CHECKING: # pragma: no cover
def lambda_coeff(xs: Sequence[CurveScalar], i: int) -> CurveScalar:
res = CurveScalar.one()
for j in range(len(xs)):
for j, xs_j in enumerate(xs):
if j != i:
inv_diff = (xs[j] - xs[i]).invert()
res = (res * xs[j]) * inv_diff
inv_diff = (xs_j - xs[i]).invert()
res = (res * xs_j) * inv_diff
return res

View File

@ -1,13 +1,15 @@
from typing import Sequence, Optional
from typing import Optional, Tuple
from .capsule import Capsule
from .curve_point import CurvePoint
from .curve_scalar import CurveScalar
from .hashing import Hash, hash_to_cfrag_verification, hash_to_cfrag_signature
from .keys import PublicKey, SecretKey, Signature
from .errors import VerificationError
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 .signing import Signature
class CapsuleFragProof(Serializable):
@ -158,11 +160,11 @@ class CapsuleFrag(Serializable):
def verify(self,
capsule: Capsule,
verifying_pk: PublicKey,
delegating_pk: PublicKey,
receiving_pk: PublicKey,
signing_pk: PublicKey,
metadata: Optional[bytes] = None,
) -> bool:
) -> 'VerifiedCapsuleFrag':
"""
Verifies the validity of this fragment.
@ -194,15 +196,49 @@ class CapsuleFrag(Serializable):
precursor = self.precursor
kfrag_id = self.kfrag_id
kfrag_signature = hash_to_cfrag_signature(kfrag_id, u1, precursor, delegating_pk, receiving_pk)
valid_kfrag_signature = kfrag_signature.verify(signing_pk, self.proof.kfrag_signature)
kfrag_message = kfrag_signature_message(kfrag_id=self.kfrag_id,
commitment=self.proof.kfrag_commitment,
precursor=self.precursor,
maybe_delegating_pk=delegating_pk,
maybe_receiving_pk=receiving_pk)
z3 = self.proof.signature
correct_reencryption_of_e = e * z3 == e2 + e1 * h
correct_reencryption_of_v = v * z3 == v2 + v1 * h
correct_rk_commitment = u * z3 == u2 + u1 * h
if not self.proof.kfrag_signature.verify(verifying_pk, kfrag_message):
raise VerificationError("Invalid KeyFrag signature")
return (valid_kfrag_signature
and correct_reencryption_of_e
and correct_reencryption_of_v
and correct_rk_commitment)
z = self.proof.signature
# TODO: if one or more of the values here are incorrect,
# we'll get the wrong `h` (since they're all hashed into it),
# so perhaps it's enough to check only one of these equations.
# See https://github.com/nucypher/rust-umbral/issues/46 for details.
correct_reencryption_of_e = e * z == e2 + e1 * h
correct_reencryption_of_v = v * z == v2 + v1 * h
correct_rk_commitment = u * z == u2 + u1 * h
if not (correct_reencryption_of_e and correct_reencryption_of_v and correct_rk_commitment):
raise VerificationError("Failed to verify reencryption proof")
return VerifiedCapsuleFrag(self)
class VerifiedCapsuleFrag:
"""
Verified capsule frag, good for decryption.
Can be cast to ``bytes``, but cannot be deserialized from bytes directly.
It can only be obtained from :py:meth:`CapsuleFrag.verify`.
"""
def __init__(self, cfrag: CapsuleFrag):
self.cfrag = cfrag
def __bytes__(self):
return bytes(self.cfrag)
def __eq__(self, other):
return self.cfrag == other.cfrag
def __hash__(self):
return hash((self.__class__, bytes(self)))
def __str__(self):
return f"{self.__class__.__name__}:{bytes(self).hex()[:16]}"

View File

@ -1,11 +1,5 @@
from . import openssl
# Global Curve Instances
SECP256R1 = openssl.Curve.from_name('secp256r1')
SECP256K1 = openssl.Curve.from_name('secp256k1')
SECP384R1 = openssl.Curve.from_name('secp384r1')
CURVES = (SECP256K1, SECP256R1, SECP384R1)
CURVE = SECP256K1

View File

@ -1,4 +1,4 @@
from typing import Optional, Tuple
from typing import Tuple
from . import openssl
from .curve import CURVE
@ -26,15 +26,6 @@ class CurvePoint(Serializable):
"""
return cls.generator() * CurveScalar.random_nonzero()
@classmethod
def from_affine(cls, affine_x: int, affine_y: int) -> 'CurvePoint':
"""
Returns a CurvePoint object from the given affine coordinates in a tuple in
the format of (x, y) and a given curve.
"""
backend_point = openssl.point_from_affine_coords(CURVE, affine_x, affine_y)
return cls(backend_point)
def to_affine(self) -> Tuple[int, int]:
"""
Returns a tuple of Python ints in the format of (x, y) that represents
@ -80,7 +71,7 @@ class CurvePoint(Serializable):
"""
Performs subtraction by adding the inverse of the `other` to the point.
"""
return (self + (-other))
return self + (-other)
def __neg__(self) -> 'CurvePoint':
"""

View File

@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, Optional, Union, Tuple
from typing import TYPE_CHECKING, Union, Tuple
from . import openssl
from .curve import CURVE
@ -37,12 +37,12 @@ class CurveScalar(Serializable):
@classmethod
def from_digest(cls, digest: 'Hash') -> 'CurveScalar':
# TODO (#39): to be replaced by the standard algroithm.
# Currently just matching what we have in RustCrypto stack
# TODO (#39): this is used in Umbral scheme itself,
# and needs to be able to return a guaranteed nonzero scalar.
# Currently just matching what we have in rust-umbral
# (taking bytes modulo curve order).
# Can produce zeros!
bn = openssl.bn_from_bytes(digest.finalize(), apply_modulus=CURVE.bn_order)
return cls(bn)
return cls(openssl.bn_from_bytes(digest.finalize(), apply_modulus=CURVE.bn_order))
@classmethod
def __take__(cls, data: bytes) -> Tuple['CurveScalar', bytes]:
@ -66,7 +66,7 @@ class CurveScalar(Serializable):
"""
Compares the two BIGNUMS or int.
"""
if type(other) == int:
if isinstance(other, int):
other = CurveScalar.from_int(other)
return openssl.bn_cmp(self._backend_bignum, other._backend_bignum) == 0
@ -83,7 +83,9 @@ class CurveScalar(Serializable):
"""
if isinstance(other, int):
other = CurveScalar.from_int(other)
return CurveScalar(openssl.bn_mul(self._backend_bignum, other._backend_bignum, CURVE.bn_order))
return CurveScalar(openssl.bn_mul(self._backend_bignum,
other._backend_bignum,
CURVE.bn_order))
def __add__(self, other : Union[int, 'CurveScalar']) -> 'CurveScalar':
"""
@ -91,7 +93,9 @@ class CurveScalar(Serializable):
"""
if isinstance(other, int):
other = CurveScalar.from_int(other)
return CurveScalar(openssl.bn_add(self._backend_bignum, other._backend_bignum, CURVE.bn_order))
return CurveScalar(openssl.bn_add(self._backend_bignum,
other._backend_bignum,
CURVE.bn_order))
def __sub__(self, other : Union[int, 'CurveScalar']) -> 'CurveScalar':
"""
@ -99,7 +103,9 @@ class CurveScalar(Serializable):
"""
if isinstance(other, int):
other = CurveScalar.from_int(other)
return CurveScalar(openssl.bn_sub(self._backend_bignum, other._backend_bignum, CURVE.bn_order))
return CurveScalar(openssl.bn_sub(self._backend_bignum,
other._backend_bignum,
CURVE.bn_order))
def invert(self) -> 'CurveScalar':
"""

View File

@ -52,18 +52,18 @@ class DEM:
def decrypt(self, nonce_and_ciphertext: bytes, authenticated_data: bytes = b"") -> bytes:
if len(nonce_and_ciphertext) < self.NONCE_SIZE:
raise ValueError(f"The ciphertext must include the nonce")
raise ValueError("The ciphertext must include the nonce")
nonce = nonce_and_ciphertext[:self.NONCE_SIZE]
ciphertext = nonce_and_ciphertext[self.NONCE_SIZE:]
# Prevent an out of bounds error deep in NaCl
if len(ciphertext) < self.TAG_SIZE:
raise ValueError(f"The authentication tag is missing or malformed")
raise ValueError("The authentication tag is missing or malformed")
try:
return xchacha_decrypt(ciphertext, authenticated_data, nonce, self._key)
except nacl.exceptions.CryptoError:
except nacl.exceptions.CryptoError as e:
raise GenericError("Decryption of ciphertext failed: "
"either someone tampered with the ciphertext or "
"you are using an incorrect decryption key.")
"you are using an incorrect decryption key.") from e

View File

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

View File

@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, Optional, Type, Iterable, Union
from typing import TYPE_CHECKING, Optional, Iterable, Union, List, cast
from cryptography.hazmat.primitives import hashes
@ -6,23 +6,24 @@ from .openssl import backend, ErrorInvalidCompressedPoint
from .curve import CURVE
from .curve_scalar import CurveScalar
from .curve_point import CurvePoint
from .keys import PublicKey, SecretKey, Signature
from .serializable import Serializable, serialize_bool
if TYPE_CHECKING: # pragma: no cover
from .key_frag import KeyFragID
from .keys import PublicKey
class Hash:
OUTPUT_SIZE = 32
def __init__(self, dst: bytes):
def __init__(self, dst: Optional[bytes] = None):
self._backend_hash_algorithm = hashes.SHA256()
self._hash = hashes.Hash(self._backend_hash_algorithm, backend=backend)
len_dst = len(dst).to_bytes(4, byteorder='big')
self.update(len_dst + dst)
if dst is not None:
len_dst = len(dst).to_bytes(4, byteorder='big')
self.update(len_dst + dst)
def update(self, data: Union[bytes, Serializable]) -> None:
self._hash.update(bytes(data))
@ -62,8 +63,9 @@ 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],
metadata: Optional[bytes] = None
) -> CurveScalar:
digest = Hash(b"CFRAG_VERIFICATION")
for point in points:
digest.update(point)
@ -72,46 +74,29 @@ def hash_to_cfrag_verification(points: Iterable[CurvePoint], metadata: Optional[
return CurveScalar.from_digest(digest)
def hash_to_cfrag_signature(kfrag_id: 'KeyFragID',
def kfrag_signature_message(kfrag_id: 'KeyFragID',
commitment: CurvePoint,
precursor: CurvePoint,
maybe_delegating_pk: Optional[PublicKey],
maybe_receiving_pk: Optional[PublicKey],
) -> 'SignatureDigest':
maybe_delegating_pk: Optional['PublicKey'],
maybe_receiving_pk: Optional['PublicKey'],
) -> bytes:
digest = SignatureDigest(b"CFRAG_SIGNATURE")
digest.update(kfrag_id)
digest.update(commitment)
digest.update(precursor)
# Have to convert to bytes manually because `mypy` is not smart enough to resolve types.
if maybe_delegating_pk:
digest.update(serialize_bool(True))
digest.update(maybe_delegating_pk)
else:
digest.update(serialize_bool(False))
delegating_part = ([serialize_bool(True), bytes(maybe_delegating_pk)]
if maybe_delegating_pk
else [serialize_bool(False)])
cast(List[Serializable], delegating_part)
if maybe_receiving_pk:
digest.update(serialize_bool(True))
digest.update(maybe_receiving_pk)
else:
digest.update(serialize_bool(False))
receiving_part = ([serialize_bool(True), bytes(maybe_receiving_pk)]
if maybe_receiving_pk
else [serialize_bool(False)])
return digest
components = ([bytes(kfrag_id), bytes(commitment), bytes(precursor)] +
delegating_part +
receiving_part)
class SignatureDigest:
def __init__(self, dst: bytes):
self._digest = Hash(dst)
def update(self, value):
self._digest.update(value)
def sign(self, sk: SecretKey) -> Signature:
return sk.sign_digest(self._digest)
def verify(self, pk: PublicKey, sig: Signature):
return sig.verify_digest(pk, self._digest)
return b''.join(components)
def unsafe_hash_to_point(dst: bytes, data: bytes) -> CurvePoint:

View File

@ -1,12 +1,14 @@
import os
from typing import Tuple, List, Optional
from typing import List, Optional
from .curve_point import CurvePoint
from .curve_scalar import CurveScalar
from .hashing import hash_to_shared_secret, hash_to_cfrag_signature, hash_to_polynomial_arg
from .keys import PublicKey, SecretKey, Signature
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 .signing import Signature, Signer
class KeyFragID(Serializable):
@ -46,27 +48,29 @@ class KeyFragProof(Serializable):
params = PARAMETERS
kfrag_precursor = base.precursor
signing_sk = base.signing_sk
signer = base.signer
delegating_pk = base.delegating_pk
receiving_pk = base.receiving_pk
commitment = params.u * kfrag_key
signature_for_receiver = hash_to_cfrag_signature(kfrag_id,
commitment,
kfrag_precursor,
delegating_pk,
receiving_pk,
).sign(signing_sk)
message_for_receiver = kfrag_signature_message(kfrag_id=kfrag_id,
commitment=commitment,
precursor=kfrag_precursor,
maybe_delegating_pk=delegating_pk,
maybe_receiving_pk=receiving_pk,
)
signature_for_receiver = signer.sign(message_for_receiver)
maybe_delegating_pk = delegating_pk if sign_delegating_key else None
maybe_receiving_pk = receiving_pk if sign_receiving_key else None
signature_for_proxy = hash_to_cfrag_signature(kfrag_id,
commitment,
kfrag_precursor,
maybe_delegating_pk,
maybe_receiving_pk
).sign(signing_sk)
message_for_proxy = kfrag_signature_message(kfrag_id=kfrag_id,
commitment=commitment,
precursor=kfrag_precursor,
maybe_delegating_pk=maybe_delegating_pk,
maybe_receiving_pk=maybe_receiving_pk,
)
signature_for_proxy = signer.sign(message_for_proxy)
return cls(commitment,
signature_for_proxy,
@ -194,10 +198,10 @@ class KeyFrag(Serializable):
return cls(kfrag_id, rk, base.precursor, proof)
def verify(self,
signing_pk: PublicKey,
verifying_pk: PublicKey,
delegating_pk: Optional[PublicKey] = None,
receiving_pk: Optional[PublicKey] = None,
) -> bool:
) -> 'VerifiedKeyFrag':
"""
Verifies the validity of this fragment.
@ -212,25 +216,51 @@ class KeyFrag(Serializable):
commitment = self.proof.commitment
precursor = self.precursor
# We check that the commitment is well-formed
if commitment != u * key:
return False
raise VerificationError("Invalid kfrag commitment")
# A shortcut, perhaps not necessary
delegating_key_missing = self.proof.delegating_key_signed and not bool(delegating_pk)
receiving_key_missing = self.proof.receiving_key_signed and not bool(receiving_pk)
if self.proof.delegating_key_signed and not bool(delegating_pk):
raise VerificationError("A signature of a delegating key was included in this kfrag, "
"but the key is not provided")
if delegating_key_missing or receiving_key_missing:
return False
if self.proof.receiving_key_signed and not bool(receiving_pk):
raise VerificationError("A signature of a receiving key was included in this kfrag, "
"but the key is not provided")
delegating_pk = delegating_pk if self.proof.delegating_key_signed else None
receiving_pk = receiving_pk if self.proof.receiving_key_signed else None
sig = hash_to_cfrag_signature(kfrag_id,
commitment,
precursor,
delegating_pk,
receiving_pk)
return sig.verify(signing_pk, self.proof.signature_for_proxy)
kfrag_message = kfrag_signature_message(kfrag_id=kfrag_id,
commitment=commitment,
precursor=precursor,
maybe_delegating_pk=delegating_pk,
maybe_receiving_pk=receiving_pk)
if not self.proof.signature_for_proxy.verify(verifying_pk, kfrag_message):
raise VerificationError("Failed to verify the kfrag signature")
return VerifiedKeyFrag(self)
class VerifiedKeyFrag:
"""
Verified kfrag, good for reencryption.
Can be cast to ``bytes``, but cannot be deserialized from bytes directly.
It can only be obtained from :py:meth:`KeyFrag.verify`.
"""
def __init__(self, kfrag: KeyFrag):
self.kfrag = kfrag
def __bytes__(self):
return bytes(self.kfrag)
def __eq__(self, other):
return self.kfrag == other.kfrag
def __hash__(self):
return hash((self.__class__, bytes(self)))
def __str__(self):
return f"{self.__class__.__name__}:{bytes(self).hex()[:16]}"
class KeyFragBase:
@ -238,7 +268,7 @@ class KeyFragBase:
def __init__(self,
delegating_sk: SecretKey,
receiving_pk: PublicKey,
signing_sk: SecretKey,
signer: Signer,
threshold: int,
):
@ -273,33 +303,9 @@ class KeyFragBase:
delegating_sk.secret_scalar() * d.invert(),
*[CurveScalar.random_nonzero() for _ in range(threshold-1)]]
self.signing_sk = signing_sk
self.signer = signer
self.precursor = precursor
self.dh_point = dh_point
self.delegating_pk = delegating_pk
self.receiving_pk = receiving_pk
self.coefficients = coefficients
def generate_kfrags(delegating_sk: SecretKey,
receiving_pk: PublicKey,
signing_sk: SecretKey,
threshold: int,
num_kfrags: int,
sign_delegating_key: bool = True,
sign_receiving_key: bool = True,
) -> List[KeyFrag]:
"""
Generates ``num_kfrags`` key fragments to pass to proxies for re-encryption.
At least ``threshold`` of them will be needed for decryption.
If ``sign_delegating_key`` or ``sign_receiving_key`` are ``True``,
the corresponding keys will have to be provided to :py:meth:`KeyFrag.verify`.
"""
base = KeyFragBase(delegating_sk, receiving_pk, signing_sk, threshold)
# Technically we could allow it, but what would be the use of these kfrags?
if num_kfrags < threshold:
raise ValueError(f"Creating less kfrags ({num_kfrags}) than threshold ({threshold}) makes them useless")
return [KeyFrag.from_base(base, sign_delegating_key, sign_receiving_key) for _ in range(num_kfrags)]

View File

@ -1,20 +1,12 @@
import os
from typing import TYPE_CHECKING, Tuple
from typing import Tuple
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives.asymmetric import utils
from cryptography.hazmat.primitives.asymmetric.ec import ECDSA
from . import openssl
from .curve import CURVE
from .curve_scalar import CurveScalar
from .curve_point import CurvePoint
from .dem import kdf
from .hashing import Hash
from .serializable import Serializable
if TYPE_CHECKING: # pragma: no cover
from .hashing import Hash
class SecretKey(Serializable):
"""
@ -45,7 +37,7 @@ class SecretKey(Serializable):
return f"{self.__class__.__name__}:..."
def __hash__(self):
raise NotImplementedError("Hashing secret objects is insecure")
raise RuntimeError("Hashing secret objects is not secure")
def secret_scalar(self):
return self._scalar_key
@ -58,66 +50,6 @@ class SecretKey(Serializable):
def __bytes__(self) -> bytes:
return bytes(self._scalar_key)
def sign_digest(self, digest: 'Hash') -> 'Signature':
signature_algorithm = ECDSA(utils.Prehashed(digest._backend_hash_algorithm))
message = digest.finalize()
backend_sk = openssl.bn_to_privkey(CURVE, self._scalar_key._backend_bignum)
signature_der_bytes = backend_sk.sign(message, signature_algorithm)
r_int, s_int = utils.decode_dss_signature(signature_der_bytes)
# Normalize s
# s is public, so no constant-timeness required here
if s_int > (CURVE.order >> 1):
s_int = CURVE.order - s_int
# Already normalized, don't waste time
r = CurveScalar.from_int(r_int, check_normalization=False)
s = CurveScalar.from_int(s_int, check_normalization=False)
return Signature(r, s)
class Signature(Serializable):
"""
Wrapper for ECDSA signatures.
"""
def __init__(self, r: CurveScalar, s: CurveScalar):
self.r = r
self.s = s
def __repr__(self):
return f"ECDSA Signature: {bytes(self).hex()[:15]}"
def verify_digest(self, verifying_key: 'PublicKey', digest: 'Hash') -> bool:
backend_pk = openssl.point_to_pubkey(CURVE, verifying_key.point()._backend_point)
signature_algorithm = ECDSA(utils.Prehashed(digest._backend_hash_algorithm))
message = digest.finalize()
signature_der_bytes = utils.encode_dss_signature(int(self.r), int(self.s))
# TODO: Raise error instead of returning boolean
try:
backend_pk.verify(signature=signature_der_bytes,
data=message,
signature_algorithm=signature_algorithm)
except InvalidSignature:
return False
return True
@classmethod
def __take__(cls, data):
(r, s), data = cls.__take_types__(data, CurveScalar, CurveScalar)
return cls(r, s), data
def __bytes__(self):
return bytes(self.r) + bytes(self.s)
def __eq__(self, other):
return self.r == other.r and self.s == other.s
class PublicKey(Serializable):
"""
@ -183,7 +115,6 @@ class SecretKeyFactory(Serializable):
tag = b"KEY_DERIVATION/" + label
key = kdf(self.__key_seed, self._DERIVED_KEY_SIZE, info=tag)
from .hashing import Hash
digest = Hash(tag)
digest.update(key)
scalar_key = CurveScalar.from_digest(digest)
@ -202,4 +133,4 @@ class SecretKeyFactory(Serializable):
return f"{self.__class__.__name__}:..."
def __hash__(self):
raise NotImplementedError("Hashing secret objects is insecure")
raise RuntimeError("Hashing secret objects is not secure")

View File

@ -1,9 +1,19 @@
from contextlib import contextmanager
from typing import Tuple
from cryptography.exceptions import InternalError
from cryptography.exceptions import InternalError, InvalidSignature
from cryptography.hazmat.backends.openssl import backend
from cryptography.hazmat.backends.openssl.ec import _EllipticCurvePrivateKey, _EllipticCurvePublicKey
from cryptography.hazmat.backends.openssl.ec import (_EllipticCurvePrivateKey,
_EllipticCurvePublicKey)
from cryptography.hazmat.primitives.asymmetric import utils
from cryptography.hazmat.primitives.asymmetric.ec import ECDSA
BACKEND_LIB = backend._lib
BACKEND_FFI = backend._ffi
def tmp_bn_ctx():
return backend._tmp_bn_ctx()
class Curve:
@ -15,9 +25,7 @@ class Curve:
"""
_supported_curves = {
415: 'secp256r1',
714: 'secp256k1',
715: 'secp384r1'
}
@staticmethod
@ -26,8 +34,8 @@ class Curve:
Returns the group of a given curve via its OpenSSL nid. This must be freed
after each use otherwise it leaks memory.
"""
group = backend._lib.EC_GROUP_new_by_curve_name(nid)
backend.openssl_assert(group != backend._ffi.NULL)
group = BACKEND_LIB.EC_GROUP_new_by_curve_name(nid)
backend.openssl_assert(group != BACKEND_FFI.NULL)
return group
@staticmethod
@ -36,8 +44,8 @@ class Curve:
Returns the order of a given curve via its OpenSSL EC_GROUP.
"""
ec_order = _bn_new()
with backend._tmp_bn_ctx() as bn_ctx:
res = backend._lib.EC_GROUP_get_order(ec_group, ec_order, bn_ctx)
with tmp_bn_ctx() as bn_ctx:
res = BACKEND_LIB.EC_GROUP_get_order(ec_group, ec_order, bn_ctx)
backend.openssl_assert(res == 1)
return ec_order
@ -46,9 +54,9 @@ class Curve:
"""
Returns the generator point of a given curve via its OpenSSL EC_GROUP.
"""
generator = backend._lib.EC_GROUP_get0_generator(ec_group)
backend.openssl_assert(generator != backend._ffi.NULL)
generator = backend._ffi.gc(generator, backend._lib.EC_POINT_clear_free)
generator = BACKEND_LIB.EC_GROUP_get0_generator(ec_group)
backend.openssl_assert(generator != BACKEND_FFI.NULL)
generator = BACKEND_FFI.gc(generator, BACKEND_LIB.EC_POINT_clear_free)
return generator
@ -58,7 +66,7 @@ class Curve:
Returns the number of bits needed to represent the order of the finite
field upon the curve is based.
"""
return backend._lib.EC_GROUP_get_degree(ec_group)
return BACKEND_LIB.EC_GROUP_get_degree(ec_group)
def __init__(self, nid: int):
"""
@ -69,8 +77,8 @@ class Curve:
try:
self.name = self._supported_curves[nid]
except KeyError:
raise NotImplementedError("Curve NID {} is not supported.".format(nid))
except KeyError as e:
raise NotImplementedError("Curve NID {} is not supported.".format(nid)) from e
self.nid = nid
@ -120,12 +128,12 @@ def _bn_new():
"""
Returns a new and initialized OpenSSL BIGNUM.
"""
new_bn = backend._lib.BN_new()
backend.openssl_assert(new_bn != backend._ffi.NULL)
new_bn = backend._ffi.gc(new_bn, backend._lib.BN_clear_free)
new_bn = BACKEND_LIB.BN_new()
backend.openssl_assert(new_bn != BACKEND_FFI.NULL)
new_bn = BACKEND_FFI.gc(new_bn, BACKEND_LIB.BN_clear_free)
# Always use constant time operations.
backend._lib.BN_set_flags(new_bn, backend._lib.BN_FLG_CONSTTIME)
BACKEND_LIB.BN_set_flags(new_bn, BACKEND_LIB.BN_FLG_CONSTTIME)
return new_bn
@ -134,11 +142,11 @@ def bn_is_normalized(check_bn, modulus):
Returns ``True`` if ``check_bn`` is in ``[0, modulus)``, ``False`` otherwise.
"""
zero = backend._int_to_bn(0)
zero = backend._ffi.gc(zero, backend._lib.BN_clear_free)
zero = BACKEND_FFI.gc(zero, BACKEND_LIB.BN_clear_free)
check_sign = backend._lib.BN_cmp(check_bn, zero)
range_check = backend._lib.BN_cmp(check_bn, modulus)
return (check_sign == 1 or check_sign == 0) and range_check == -1
check_sign = BACKEND_LIB.BN_cmp(check_bn, zero)
range_check = BACKEND_LIB.BN_cmp(check_bn, modulus)
return check_sign in (0, 1) and range_check == -1
def bn_from_int(py_int: int, check_modulus=None):
@ -147,12 +155,12 @@ def bn_from_int(py_int: int, check_modulus=None):
provided, it will check if the Python integer is within ``[0, modulus)``.
"""
conv_bn = backend._int_to_bn(py_int)
conv_bn = backend._ffi.gc(conv_bn, backend._lib.BN_clear_free)
conv_bn = BACKEND_FFI.gc(conv_bn, BACKEND_LIB.BN_clear_free)
if check_modulus and not bn_is_normalized(conv_bn, check_modulus):
raise ValueError(f"The Python integer given ({py_int}) is not under the provided modulus.")
backend._lib.BN_set_flags(conv_bn, backend._lib.BN_FLG_CONSTTIME)
BACKEND_LIB.BN_set_flags(conv_bn, BACKEND_LIB.BN_FLG_CONSTTIME)
return conv_bn
@ -161,8 +169,8 @@ def bn_from_bytes(bytes_seq: bytes, check_modulus=None, apply_modulus=None):
Converts the given byte sequence to an OpenSSL BIGNUM.
"""
bn = _bn_new()
backend._lib.BN_bin2bn(bytes_seq, len(bytes_seq), bn)
backend.openssl_assert(bn != backend._ffi.NULL)
BACKEND_LIB.BN_bin2bn(bytes_seq, len(bytes_seq), bn)
backend.openssl_assert(bn != BACKEND_FFI.NULL)
if check_modulus and not bn_is_normalized(bn, check_modulus):
raise ValueError(f"The integer encoded with given bytes ({repr(bytes_seq)}) "
@ -170,12 +178,12 @@ def bn_from_bytes(bytes_seq: bytes, check_modulus=None, apply_modulus=None):
if apply_modulus:
bignum =_bn_new()
with backend._tmp_bn_ctx() as bn_ctx:
res = backend._lib.BN_mod(bignum, bn, apply_modulus, bn_ctx)
with tmp_bn_ctx() as bn_ctx:
res = BACKEND_LIB.BN_mod(bignum, bn, apply_modulus, bn_ctx)
backend.openssl_assert(res == 1)
return bignum
else:
return bn
return bn
def bn_to_bytes(bn, length: int):
@ -186,17 +194,17 @@ def bn_to_bytes(bn, length: int):
"""
# Sanity check, CurveScalar ensures it won't happen.
bn_num_bytes = backend._lib.BN_num_bytes(bn)
bn_num_bytes = BACKEND_LIB.BN_num_bytes(bn)
assert bn_num_bytes <= length, f"Input BIGNUM doesn't fit in {length} B"
bin_ptr = backend._ffi.new("unsigned char []", length)
bin_len = backend._lib.BN_bn2bin(bn, bin_ptr)
return bytes.rjust(backend._ffi.buffer(bin_ptr, bin_len)[:], length, b'\0')
bin_ptr = BACKEND_FFI.new("unsigned char []", length)
bin_len = BACKEND_LIB.BN_bn2bin(bn, bin_ptr)
return bytes.rjust(BACKEND_FFI.buffer(bin_ptr, bin_len)[:], length, b'\0')
def bn_random_nonzero(modulus):
one = backend._lib.BN_value_one()
one = BACKEND_LIB.BN_value_one()
# TODO: in most cases, we want this number to be secret.
# OpenSSL 1.1.1 has `BN_priv_rand_range()`, but it is not
@ -205,37 +213,37 @@ def bn_random_nonzero(modulus):
# Calculate `modulus - 1`
modulus_minus_1 = _bn_new()
res = backend._lib.BN_sub(modulus_minus_1, modulus, one)
res = BACKEND_LIB.BN_sub(modulus_minus_1, modulus, one)
backend.openssl_assert(res == 1)
# Get a random in range `[0, modulus - 1)`
new_rand_bn = _bn_new()
res = backend._lib.BN_rand_range(new_rand_bn, modulus_minus_1)
res = BACKEND_LIB.BN_rand_range(new_rand_bn, modulus_minus_1)
backend.openssl_assert(res == 1)
# Turn it into a random in range `[1, modulus)`
op_sum = _bn_new()
res = backend._lib.BN_add(op_sum, new_rand_bn, one)
res = BACKEND_LIB.BN_add(op_sum, new_rand_bn, one)
backend.openssl_assert(res == 1)
return op_sum
def _bn_size(bn):
return backend._lib.BN_num_bytes(bn)
return BACKEND_LIB.BN_num_bytes(bn)
def bn_to_int(bn):
def bn_to_int(bn) -> int:
return backend._bn_to_int(bn)
def bn_cmp(bn1, bn2):
# -1 less than, 0 is equal to, 1 is greater than
return backend._lib.BN_cmp(bn1, bn2)
return BACKEND_LIB.BN_cmp(bn1, bn2)
def bn_one():
return backend._lib.BN_value_one()
return BACKEND_LIB.BN_value_one()
def bn_is_zero(bn):
@ -244,47 +252,47 @@ def bn_is_zero(bn):
def bn_invert(bn, modulus):
with backend._tmp_bn_ctx() as bn_ctx:
inv = backend._lib.BN_mod_inverse(backend._ffi.NULL, bn, modulus, bn_ctx)
backend.openssl_assert(inv != backend._ffi.NULL)
inv = backend._ffi.gc(inv, backend._lib.BN_clear_free)
with tmp_bn_ctx() as bn_ctx:
inv = BACKEND_LIB.BN_mod_inverse(BACKEND_FFI.NULL, bn, modulus, bn_ctx)
backend.openssl_assert(inv != BACKEND_FFI.NULL)
inv = BACKEND_FFI.gc(inv, BACKEND_LIB.BN_clear_free)
return inv
def bn_sub(bn1, bn2, modulus):
diff = _bn_new()
with backend._tmp_bn_ctx() as bn_ctx:
res = backend._lib.BN_mod_sub(diff, bn1, bn2, modulus, bn_ctx)
with tmp_bn_ctx() as bn_ctx:
res = BACKEND_LIB.BN_mod_sub(diff, bn1, bn2, modulus, bn_ctx)
backend.openssl_assert(res == 1)
return diff
def bn_add(bn1, bn2, modulus):
op_sum = _bn_new()
with backend._tmp_bn_ctx() as bn_ctx:
res = backend._lib.BN_mod_add(op_sum, bn1, bn2, modulus, bn_ctx)
with tmp_bn_ctx() as bn_ctx:
res = BACKEND_LIB.BN_mod_add(op_sum, bn1, bn2, modulus, bn_ctx)
backend.openssl_assert(res == 1)
return op_sum
def bn_mul(bn1, bn2, modulus):
product = _bn_new()
with backend._tmp_bn_ctx() as bn_ctx:
res = backend._lib.BN_mod_mul(product, bn1, bn2, modulus, bn_ctx)
with tmp_bn_ctx() as bn_ctx:
res = BACKEND_LIB.BN_mod_mul(product, bn1, bn2, modulus, bn_ctx)
backend.openssl_assert(res == 1)
return product
def bn_to_privkey(curve: Curve, bn):
ec_key = backend._lib.EC_KEY_new()
backend.openssl_assert(ec_key != backend._ffi.NULL)
ec_key = backend._ffi.gc(ec_key, backend._lib.EC_KEY_free)
ec_key = BACKEND_LIB.EC_KEY_new()
backend.openssl_assert(ec_key != BACKEND_FFI.NULL)
ec_key = BACKEND_FFI.gc(ec_key, BACKEND_LIB.EC_KEY_free)
set_group_result = backend._lib.EC_KEY_set_group(ec_key, curve.ec_group)
set_group_result = BACKEND_LIB.EC_KEY_set_group(ec_key, curve.ec_group)
backend.openssl_assert(set_group_result == 1)
set_privkey_result = backend._lib.EC_KEY_set_private_key(ec_key, bn)
set_privkey_result = BACKEND_LIB.EC_KEY_set_private_key(ec_key, bn)
backend.openssl_assert(set_privkey_result == 1)
evp_pkey = backend._ec_cdata_to_evp_pkey(ec_key)
@ -301,30 +309,13 @@ def _point_new(ec_group):
Returns a new and initialized OpenSSL EC_POINT given the group of a curve.
If __curve_nid is provided, it retrieves the group from the curve provided.
"""
new_point = backend._lib.EC_POINT_new(ec_group)
backend.openssl_assert(new_point != backend._ffi.NULL)
new_point = backend._ffi.gc(new_point, backend._lib.EC_POINT_clear_free)
new_point = BACKEND_LIB.EC_POINT_new(ec_group)
backend.openssl_assert(new_point != BACKEND_FFI.NULL)
new_point = BACKEND_FFI.gc(new_point, BACKEND_LIB.EC_POINT_clear_free)
return new_point
def point_from_affine_coords(curve: Curve, affine_x: int, affine_y: int):
"""
Returns an EC_POINT given the group of a curve and the affine coordinates
provided.
"""
bn_affine_x = bn_from_int(affine_x)
bn_affine_y = bn_from_int(affine_y)
new_point = _point_new(curve.ec_group)
with backend._tmp_bn_ctx() as bn_ctx:
res = backend._lib.EC_POINT_set_affine_coordinates_GFp(
curve.ec_group, new_point, bn_affine_x, bn_affine_y, bn_ctx
)
backend.openssl_assert(res == 1)
return new_point
def point_to_affine_coords(curve: Curve, point) -> Tuple[int, int]:
"""
Returns the affine coordinates of a given point on the provided ec_group.
@ -332,11 +323,14 @@ def point_to_affine_coords(curve: Curve, point) -> Tuple[int, int]:
affine_x = _bn_new()
affine_y = _bn_new()
with backend._tmp_bn_ctx() as bn_ctx:
res = backend._lib.EC_POINT_get_affine_coordinates_GFp(
curve.ec_group, point, affine_x, affine_y, bn_ctx
)
backend.openssl_assert(res == 1)
try:
with tmp_bn_ctx() as bn_ctx:
res = BACKEND_LIB.EC_POINT_get_affine_coordinates_GFp(
curve.ec_group, point, affine_x, affine_y, bn_ctx
)
backend.openssl_assert(res == 1)
except InternalError as e:
raise ValueError("Cannot get affine coordinates of an identity point")
return bn_to_int(affine_x), bn_to_int(affine_y)
@ -352,8 +346,8 @@ class ErrorInvalidPointEncoding(Exception):
def point_from_bytes(curve: Curve, data):
point = _point_new(curve.ec_group)
try:
with backend._tmp_bn_ctx() as bn_ctx:
res = backend._lib.EC_POINT_oct2point(curve.ec_group, point, data, len(data), bn_ctx);
with tmp_bn_ctx() as bn_ctx:
res = BACKEND_LIB.EC_POINT_oct2point(curve.ec_group, point, data, len(data), bn_ctx)
backend.openssl_assert(res == 1)
except InternalError as e:
# We want to catch specific InternalExceptions.
@ -361,35 +355,35 @@ def point_from_bytes(curve: Curve, data):
# There is also EC_R_POINT_IS_NOT_ON_CURVE (code 107),
# but somehow it is never triggered during deserialization.
if e.err_code[0].reason == 110: # EC_R_INVALID_COMPRESSED_POINT
raise ErrorInvalidCompressedPoint
elif e.err_code[0].reason == 102: # EC_R_INVALID_ENCODING
raise ErrorInvalidPointEncoding
else:
# Any other exception, we raise it.
# (although at the moment I'm not sure what should one do to cause it)
raise e # pragma: no cover
raise ErrorInvalidCompressedPoint from e
if e.err_code[0].reason == 102: # EC_R_INVALID_ENCODING
raise ErrorInvalidPointEncoding from e
# Any other exception, we raise it.
# (although at the moment I'm not sure what should one do to cause it)
raise # pragma: no cover
return point
def point_to_bytes_compressed(curve: Curve, point):
point_conversion_form = backend._lib.POINT_CONVERSION_COMPRESSED
point_conversion_form = BACKEND_LIB.POINT_CONVERSION_COMPRESSED
size = curve.field_element_size + 1 # compressed point size
bin_ptr = backend._ffi.new("unsigned char[]", size)
with backend._tmp_bn_ctx() as bn_ctx:
bin_len = backend._lib.EC_POINT_point2oct(
bin_ptr = BACKEND_FFI.new("unsigned char[]", size)
with tmp_bn_ctx() as bn_ctx:
bin_len = BACKEND_LIB.EC_POINT_point2oct(
curve.ec_group, point, point_conversion_form,
bin_ptr, size, bn_ctx
)
backend.openssl_assert(bin_len != 0)
return bytes(backend._ffi.buffer(bin_ptr, bin_len)[:])
return bytes(BACKEND_FFI.buffer(bin_ptr, bin_len)[:])
def point_eq(curve: Curve, point1, point2):
with backend._tmp_bn_ctx() as bn_ctx:
is_equal = backend._lib.EC_POINT_cmp(curve.ec_group, point1, point2, bn_ctx)
with tmp_bn_ctx() as bn_ctx:
is_equal = BACKEND_LIB.EC_POINT_cmp(curve.ec_group, point1, point2, bn_ctx)
backend.openssl_assert(is_equal != -1)
# 1 is not-equal, 0 is equal, -1 is error
@ -398,27 +392,27 @@ def point_eq(curve: Curve, point1, point2):
def point_mul_bn(curve: Curve, point, bn):
prod = _point_new(curve.ec_group)
with backend._tmp_bn_ctx() as bn_ctx:
res = backend._lib.EC_POINT_mul(curve.ec_group, prod, backend._ffi.NULL, point, bn, bn_ctx)
with tmp_bn_ctx() as bn_ctx:
res = BACKEND_LIB.EC_POINT_mul(curve.ec_group, prod, BACKEND_FFI.NULL, point, bn, bn_ctx)
backend.openssl_assert(res == 1)
return prod
def point_add(curve: Curve, point1, point2):
op_sum = _point_new(curve.ec_group)
with backend._tmp_bn_ctx() as bn_ctx:
res = backend._lib.EC_POINT_add(curve.ec_group, op_sum, point1, point2, bn_ctx)
with tmp_bn_ctx() as bn_ctx:
res = BACKEND_LIB.EC_POINT_add(curve.ec_group, op_sum, point1, point2, bn_ctx)
backend.openssl_assert(res == 1)
return op_sum
def point_neg(curve: Curve, point):
inv = backend._lib.EC_POINT_dup(point, curve.ec_group)
backend.openssl_assert(inv != backend._ffi.NULL)
inv = backend._ffi.gc(inv, backend._lib.EC_POINT_clear_free)
inv = BACKEND_LIB.EC_POINT_dup(point, curve.ec_group)
backend.openssl_assert(inv != BACKEND_FFI.NULL)
inv = BACKEND_FFI.gc(inv, BACKEND_LIB.EC_POINT_clear_free)
with backend._tmp_bn_ctx() as bn_ctx:
res = backend._lib.EC_POINT_invert(curve.ec_group, inv, bn_ctx)
with tmp_bn_ctx() as bn_ctx:
res = BACKEND_LIB.EC_POINT_invert(curve.ec_group, inv, bn_ctx)
backend.openssl_assert(res == 1)
return inv
@ -426,15 +420,45 @@ def point_neg(curve: Curve, point):
def point_to_pubkey(curve: Curve, point):
ec_key = backend._lib.EC_KEY_new()
backend.openssl_assert(ec_key != backend._ffi.NULL)
ec_key = backend._ffi.gc(ec_key, backend._lib.EC_KEY_free)
ec_key = BACKEND_LIB.EC_KEY_new()
backend.openssl_assert(ec_key != BACKEND_FFI.NULL)
ec_key = BACKEND_FFI.gc(ec_key, BACKEND_LIB.EC_KEY_free)
set_group_result = backend._lib.EC_KEY_set_group(ec_key, curve.ec_group)
set_group_result = BACKEND_LIB.EC_KEY_set_group(ec_key, curve.ec_group)
backend.openssl_assert(set_group_result == 1)
set_pubkey_result = backend._lib.EC_KEY_set_public_key(ec_key, point)
set_pubkey_result = BACKEND_LIB.EC_KEY_set_public_key(ec_key, point)
backend.openssl_assert(set_pubkey_result == 1)
evp_pkey = backend._ec_cdata_to_evp_pkey(ec_key)
return _EllipticCurvePublicKey(backend, ec_key, evp_pkey)
#
# Signing
#
def ecdsa_sign(curve: Curve,
secret_bn,
prehashed_message: bytes,
hash_algorithm
) -> Tuple[int, int]:
signature_algorithm = ECDSA(utils.Prehashed(hash_algorithm))
private_key = bn_to_privkey(curve, secret_bn)
signature_der_bytes = private_key.sign(prehashed_message, signature_algorithm)
r_int, s_int = utils.decode_dss_signature(signature_der_bytes)
return r_int, s_int
def ecdsa_verify(curve: Curve, sig_r: int, sig_s: int, public_point,
prehashed_message: bytes, hash_algorithm) -> bool:
signature_algorithm = ECDSA(utils.Prehashed(hash_algorithm))
public_key = point_to_pubkey(curve, public_point)
signature_der_bytes = utils.encode_dss_signature(sig_r, sig_s)
try:
public_key.verify(signature=signature_der_bytes,
data=prehashed_message,
signature_algorithm=signature_algorithm)
except InvalidSignature:
return False
return True

View File

@ -1,10 +1,11 @@
from typing import Tuple, Optional, Sequence
from typing import Tuple, Optional, Sequence, List
from .capsule import Capsule
from .capsule_frag import CapsuleFrag
from .capsule_frag import VerifiedCapsuleFrag, CapsuleFrag
from .dem import DEM
from .keys import PublicKey, SecretKey
from .key_frag import KeyFrag
from .key_frag import VerifiedKeyFrag, KeyFrag, KeyFragBase
from .signing import Signer
def encrypt(pk: PublicKey, plaintext: bytes) -> Tuple[Capsule, bytes]:
@ -30,7 +31,39 @@ def decrypt_original(sk: SecretKey, capsule: Capsule, ciphertext: bytes) -> byte
return dem.decrypt(ciphertext, authenticated_data=bytes(capsule))
def reencrypt(capsule: Capsule, kfrag: KeyFrag, metadata: Optional[bytes] = None) -> CapsuleFrag:
def generate_kfrags(delegating_sk: SecretKey,
receiving_pk: PublicKey,
signer: Signer,
threshold: int,
num_kfrags: int,
sign_delegating_key: bool = True,
sign_receiving_key: bool = True,
) -> List[VerifiedKeyFrag]:
"""
Generates ``num_kfrags`` key fragments to pass to proxies for re-encryption.
At least ``threshold`` of them will be needed for decryption.
If ``sign_delegating_key`` or ``sign_receiving_key`` are ``True``,
the corresponding keys will have to be provided to :py:meth:`KeyFrag.verify`.
"""
base = KeyFragBase(delegating_sk, receiving_pk, signer, threshold)
# Technically we could allow it, but what would be the use of these kfrags?
if num_kfrags < threshold:
raise ValueError(f"Creating less kfrags ({num_kfrags}) "
f"than threshold ({threshold}) makes them useless")
kfrags = [KeyFrag.from_base(base, sign_delegating_key, sign_receiving_key)
for _ in range(num_kfrags)]
# Make them verified - we know they're good.
return [VerifiedKeyFrag(kfrag) for kfrag in kfrags]
def reencrypt(capsule: Capsule,
kfrag: VerifiedKeyFrag,
metadata: Optional[bytes] = None
) -> VerifiedCapsuleFrag:
"""
Creates a capsule fragment using the given key fragment.
Capsule fragments can later be used to decrypt the ciphertext.
@ -38,20 +71,30 @@ def reencrypt(capsule: Capsule, kfrag: KeyFrag, metadata: Optional[bytes] = None
If `metadata` is provided, it will have to be used for verification in
:py:meth:`CapsuleFrag.verify`.
"""
return CapsuleFrag.reencrypted(capsule, kfrag, metadata)
# 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))
def decrypt_reencrypted(decrypting_sk: SecretKey,
delegating_pk: PublicKey,
capsule: Capsule,
cfrags: Sequence[CapsuleFrag],
verified_cfrags: Sequence[VerifiedCapsuleFrag],
ciphertext: bytes,
) -> bytes:
"""
Decrypts the ciphertext using the original capsule and the reencrypted capsule fragments.
"""
# We could let duck typing do its work,
# but it's better to make a common error more understandable.
for cfrag in verified_cfrags:
if isinstance(cfrag, CapsuleFrag) and not isinstance(cfrag, VerifiedCapsuleFrag):
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)
dem = DEM(bytes(key_seed))
return dem.decrypt(ciphertext, authenticated_data=bytes(capsule))

View File

@ -25,7 +25,8 @@ class Serializable(ABC):
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 from a bytestring of size {len(data)}")
raise ValueError(f"{cls} cannot take {size} bytes "
f"from a bytestring of size {len(data)}")
return data[:size], data[size:]
@classmethod
@ -70,5 +71,6 @@ def take_bool(data: bytes) -> Tuple[bool, bytes]:
elif bool_bytes == b'\x00':
b = False
else:
raise ValueError(f"Incorrectly serialized boolean; expected b'\\x00' or b'\\x01', got {repr(bool_bytes)}")
raise ValueError("Incorrectly serialized boolean; "
f"expected b'\\x00' or b'\\x01', got {repr(bool_bytes)}")
return b, data

109
umbral/signing.py Normal file
View File

@ -0,0 +1,109 @@
from . import openssl
from .curve import CURVE
from .curve_scalar import CurveScalar
from .hashing import Hash
from .keys import SecretKey, PublicKey
from .serializable import Serializable
def digest_for_signing(message: bytes) -> Hash:
# Not using a DST here to make life easier for third-party verifiers
digest = Hash()
digest.update(message)
return digest
class Signer:
"""
An object possessing the capability to create signatures.
For safety reasons serialization is prohibited.
"""
def __init__(self, secret_key: SecretKey):
self.__secret_key = secret_key
def sign_digest(self, digest: 'Hash') -> 'Signature':
secret_bn = self.__secret_key.secret_scalar()._backend_bignum
r_int, s_int = openssl.ecdsa_sign(curve=CURVE,
secret_bn=secret_bn,
prehashed_message=digest.finalize(),
hash_algorithm=digest._backend_hash_algorithm)
# Normalize s. This is a non-malleability measure, which OpenSSL doesn't do.
# See Bitcoin's BIP-0062 for more details:
# https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#Low_S_values_in_signatures
# s is public, so no constant-timeness required here
if s_int > (CURVE.order >> 1):
s_int = CURVE.order - s_int
# Already normalized, don't waste time
r = CurveScalar.from_int(r_int, check_normalization=False)
s = CurveScalar.from_int(s_int, check_normalization=False)
return Signature(r, s)
def sign(self, message: bytes) -> 'Signature':
"""
Hashes and signs the message.
"""
return self.sign_digest(digest_for_signing(message))
def verifying_key(self) -> PublicKey:
"""
Returns the public verification key corresponding to the secret key used for signing.
"""
return PublicKey.from_secret_key(self.__secret_key)
def __str__(self):
return f"{self.__class__.__name__}:..."
def __hash__(self):
raise RuntimeError(f"{self.__class__.__name__} objects do not support hashing")
def __bytes__(self):
raise RuntimeError(f"{self.__class__.__name__} objects do not support serialization")
class Signature(Serializable):
"""
Wrapper for ECDSA signatures.
"""
def __init__(self, r: CurveScalar, s: CurveScalar):
self.r = r
self.s = s
def verify_digest(self, verifying_key: 'PublicKey', digest: 'Hash') -> bool:
return openssl.ecdsa_verify(curve=CURVE,
sig_r=int(self.r),
sig_s=int(self.s),
public_point=verifying_key.point()._backend_point,
prehashed_message=digest.finalize(),
hash_algorithm=digest._backend_hash_algorithm)
def verify(self, verifying_key: PublicKey, message: bytes) -> bool:
"""
Returns ``True`` if the ``message`` was signed by someone possessing the secret counterpart
to ``verifying_key``.
"""
digest = digest_for_signing(message)
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 __bytes__(self):
return bytes(self.r) + bytes(self.s)
def __str__(self):
return f"{self.__class__.__name__}:{bytes(self).hex()[:16]}"
def __eq__(self, other):
return self.r == other.r and self.s == other.s
def __hash__(self) -> int:
return hash((self.__class__, bytes(self)))

View File

@ -1,7 +1,9 @@
import json
import os
from umbral import SecretKey, PublicKey, encrypt, generate_kfrags, reencrypt
from umbral import (
SecretKey, PublicKey, Signer, KeyFrag, CapsuleFrag,
encrypt, generate_kfrags, reencrypt)
from umbral.curve_scalar import CurveScalar
from umbral.curve_point import CurvePoint
from umbral.hashing import Hash, unsafe_hash_to_point
@ -15,9 +17,7 @@ from umbral.dem import DEM, kdf
def hexlify(data):
if isinstance(data, int):
return hex(data)[2:]
try:
return data.to_bytes().hex()
except AttributeError:
else:
return bytes(data).hex()
@ -50,7 +50,7 @@ receiving_pk = PublicKey.from_secret_key(receiving_sk)
kfrags = generate_kfrags(delegating_sk=delegating_sk,
receiving_pk=receiving_pk,
signing_sk=signing_sk,
signer=Signer(signing_sk),
threshold=6,
num_kfrags=10,
)
@ -59,7 +59,7 @@ plain_data = b'peace at dawn'
capsule, ciphertext = encrypt(delegating_pk, plain_data)
cfrag = reencrypt(capsule, kfrags[0])
cfrag = CapsuleFrag.from_bytes(bytes(reencrypt(capsule, kfrags[0])))
points = [capsule.point_e, cfrag.point_e1, cfrag.proof.point_e2,
capsule.point_v, cfrag.point_v1, cfrag.proof.point_v2,
cfrag.proof.kfrag_commitment, cfrag.proof.kfrag_pok]
@ -207,7 +207,8 @@ create_test_vector_file(vector_suite, 'vectors_unsafe_hash_to_point.json', gener
vectors = list()
for kfrag in kfrags:
assert kfrag.verify(verifying_pk, delegating_pk, receiving_pk)
kfrag = KeyFrag.from_bytes(bytes(kfrag))
kfrag.verify(verifying_pk, delegating_pk, receiving_pk)
json_input = {'kfrag': hexlify(kfrag)}

View File

@ -2,51 +2,51 @@
"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')`",
"params": "default",
"capsule": "02a03893438c0502dd13818f65c039b2ef4fce33bfed150c6ad166554b4e8a51c2028da9ebf8cc0966bc010152fd3917a8f12dfff0af3b06e34e17f300d622893159367ce07602310c78fc65a8b6115d75f65d362abdf2fd799bebc55aa0cda44dbb",
"capsule": "02558f1de19a58e73a94e8fbbc6d3b1de2d312d90746ea74cb29f046943bf5787102906780e9484aec2102a01a157f10ced5aec952cd00631d94d5ea2edfa9b6808361b109353b0827b7e4013ab92a70eb3337a37f6fe34b3ccb058592caa246c974",
"metadata": "6b667261675f6d65746164617461",
"verifying_pk": "03f24761ac8b02de08ad1622d023f669d6214c3bab81a33087ed3ec5505e4d43db",
"delegating_pk": "03a73623a2e72fd52b2d313214c7495580c14fe6cd8de7ad0d63bbfbfd6fb6bd4d",
"receiving_pk": "02952a1903b9c929f0d93d935b34b272ea25a84833a04e22d887f27bc3bf0cc409",
"verifying_pk": "030b95b3f249297824b32d3391392d62a9aff32e8698fa78c7e8ce4a9d17071f56",
"delegating_pk": "02d67029bb92522059225d190038230c23466e28d132d48f714f9098168a562b8a",
"receiving_pk": "03b0d0243e8954b408047eee3b09b5ed132ccc25ec70e99fc74b6e9f54e5ecf9c7",
"vectors": [
{
"kfrag": "3f3453856117dedb3d7518d0435c04b10734700c8aca48fa5a8b85eded515cdffefbefddcd3cb728dcc6d05061b5e201aa1cef55ce89e94c34b6887b307d5ab2025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb7490374d2e59ea274c8011f6bf26ca8fe8eb8e7837cafed8547485e3fde6ffe0368e98d26b0d7e12095941daffae8ca429147d9686dc3285ea182c0d7b15db41c7afd3fae3c88100707ef28b53d7cf93961dc509864eb319f0cce274544306c96099b5792af0b820bf758f5c8f2a69cfc1e0a5f91fab96ab82c10c7740602f90efdd315ff5dbd07323526ca1c9a93132b4d008fdf1796a55b92e2fe75c544652256c80101",
"cfrag": "02ecbc4dc0aed60efa211c7bf0e238593d292a042373b891928bcee459151c2f440281171b7e330ebd097575dadb210e8a405bc162e293881457a301da03f7571c7a3f3453856117dedb3d7518d0435c04b10734700c8aca48fa5a8b85eded515cdf025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb74902889960dbc86c87e5a1275f2c1df31e43ed0b3e4616126845a7394b27a83f6d1e03f0b7c60a76c7c9f590c9ac2d1f0ebf6aecaa9d371e2dcd04e19caa134e9c1b530374d2e59ea274c8011f6bf26ca8fe8eb8e7837cafed8547485e3fde6ffe0368e902660d6c64d749050f027983ebd4631838373a47c887537bea95aa139d53bfe38ea532a6b7464b0b5f45522c495a2b8a772b98c4950aa2bb830f6602e7636a1bd95792af0b820bf758f5c8f2a69cfc1e0a5f91fab96ab82c10c7740602f90efdd315ff5dbd07323526ca1c9a93132b4d008fdf1796a55b92e2fe75c544652256c8"
"kfrag": "2565903141941fecb01fb92dfbdd66a0a1d4eb94d3226beb2f7adcbb8282ffd0b7934320225868d8f9ace7784fd3ae0c20a09c5a2b698fae0554d71b1f016670036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b026cb94f302809d19aacc81da19f2156db9c610498310b930d7787d8a2366dadd3d977c6befcff3088016d5d9050f4932cb4a9ce483dd7cf27e1f157f8d23f598248d5027a8961a09c874c82a8c479fbf074675790edff37e6927a7d4ea269e600685dc9469c87e6d9d469656b2f40b33e6d6813ec3519fc065a4ffc0eeb8abd324c1914451341cdb7abd7c5aa01adade220b2894be5a33caa9ee7d3b4637bdccf0101",
"cfrag": "025e6a08eb9376adfea3a92e05fea213c493fe051461fdf5639a7108e8687eeacf03fcfdb46bf83a68e0d674e7d5b7c0365c8fa05dd418f2ba1a4aea2abcbcd12a192565903141941fecb01fb92dfbdd66a0a1d4eb94d3226beb2f7adcbb8282ffd0036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b02c6348049682bd1e8fc6d4fa0b60a44a50944d497fffc63a4a1c15d7a6bf707e103e2966028c41d81f1bbae136279db8edca069a26183fec4bd12f253ae92d03044026cb94f302809d19aacc81da19f2156db9c610498310b930d7787d8a2366dadd30360288316249f090306991e6a604d8f0da56ebe3275abf4353f223391c95ad06db8c00bc92cef528117fbe10f7e70310aca5f0223a5d22c6ae36e6d26a116f0df685dc9469c87e6d9d469656b2f40b33e6d6813ec3519fc065a4ffc0eeb8abd324c1914451341cdb7abd7c5aa01adade220b2894be5a33caa9ee7d3b4637bdccf"
},
{
"kfrag": "d15ac028be9938b5e2d7a9fb9957b416db538a11d141d320a5f656ab4e3cb0cab3a2ddd2672f3514e7da2f29cbe3815c83b6d704e09d595a68fdd5aeff52ef8a025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb749033d6984555d41614a2ba08c2182e27a7105cb60ed414c9732a156c3e44d196dd3507335e3781d6c9a4543e3ae81a4c7538cf280292c9c0d92e4f513e4e073721f2f72a1ea0e69912e21754e32edf9cefd3a96183f5501266bedb301f291709ffb25ed5213660db4c8bfc086e794650455bfde1251f92e1fe49f1feb016ad44fe859b342964db14dddfa6a33eba53021149d8663d306850b8907f9dabf4a5d3ecd0101",
"cfrag": "03a98cc0d807a4f2b6183dfb9b7ff589f376183b33f88ae07a50323edaac946d9b02fc04f97b7afa5c8664082e4433a24184d54724e9e18afd44922bd052bea87e49d15ac028be9938b5e2d7a9fb9957b416db538a11d141d320a5f656ab4e3cb0ca025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb74903bf5c856a6b918df7a70a10db9854d43009ee517e7fc4403185c99eb05f42638c0213c86d5b3f3816a4807bf797731b8b23ef2563e6698e9380a0ee4044755504a9033d6984555d41614a2ba08c2182e27a7105cb60ed414c9732a156c3e44d196dd303de157a0d01a46d89e57d5586bee352b17bb123ea52dea60ef283456fa50cb5a98eca7141ac4bd117511c10922216c69e548c5005e49571087fefb69d50cecc8225ed5213660db4c8bfc086e794650455bfde1251f92e1fe49f1feb016ad44fe859b342964db14dddfa6a33eba53021149d8663d306850b8907f9dabf4a5d3ecd"
"kfrag": "ae85e679cf44b1877ff36e48c4965144724f17cc7605f53e50007aabbc2c7ab3871d04e6b5666b81a99ba0c6a70ba8675f2122a39eaec04a62c8496d5149ac9d036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b02e3e7722bffcb6674987b0089f6b8ac0d6cb220d9661edb5ed9dca5fcd33d5bdb1e41b92c9a8e4346b2dd1072389e4ce36a6230eecb59b7e0e9d84f35bf14f0f23014a1263e17d07a94c5d9c248c692686c745df3c5d11566b9ca17ec62e59a5265749ef2701a58483bd106be985c3e4a03750e3c0c21424cf7f59682224c4f6e29c242904ade5ac377974eaa662b6ed6eed13fd30509b9cb332b4fa89043a2a70101",
"cfrag": "039715d1c5678c3fb28900a95332eb37beedeffc8cb4862cd11248bcf6ee0c316002fe9dd2d4e6afc607a0ddcfba92ac81b008feb182b516898659a3fc9ec53dae4aae85e679cf44b1877ff36e48c4965144724f17cc7605f53e50007aabbc2c7ab3036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b02a745ff6a18340f1c7657c03e5ea3523971604f71ecaae1183a7b88cde1780c4003d8b1d2e220512be9f375c518e4e73ff73f0e49d3b12b328ccd2706ad59dd6e7702e3e7722bffcb6674987b0089f6b8ac0d6cb220d9661edb5ed9dca5fcd33d5bdb036e2625fb8bd22e84f82ffa8009e8366b141be7b99a0fc74cfed82558d98b3c1183b0dfa5571608f200d7a1c546677a8186535306a210eec6add04429eeb81b3f65749ef2701a58483bd106be985c3e4a03750e3c0c21424cf7f59682224c4f6e29c242904ade5ac377974eaa662b6ed6eed13fd30509b9cb332b4fa89043a2a7"
},
{
"kfrag": "f780e25b78581e70d28f102264e1f04b482eb9ecd45c188f2f9cd90026627f904b903c6da7529805e1d91fbaebc384588083e2c5f235c31e7f768b8d0df43513025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb749032f4d57c068c255281703ba6d449345e2f6240a4b8b3047209ff2aca5edec0ad2394020e796b7b9f9130c0ab02b4c8151d050f38f3d0ff5f38dc164e4d254ad2737758c4b67c0b54267b113692f11a410ffc5fe3e41c2f8ec9246854baa63dbfaebb58f0cc89c528e119788c521bbfa8dd33c69129c1a05ed454d7534224c9df35007ef11b38769ee8d2788c72745e50e14e32b0c2b9d988e2550dd3f89cbd6b50101",
"cfrag": "03ec5772868ec577fb8e054a7f0717dc56b7eded542a06c593ac7166093ae196c10394fc79d3d700a9536b10233ad1cf145a533a43d35c48028c7b2a6137a8d6097ff780e25b78581e70d28f102264e1f04b482eb9ecd45c188f2f9cd90026627f90025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb74903ad08b8333f7a44acf71a2738e7e6213d715d7cee759a825abce04b72ca8d56d0033eba87004940ec899c4eb593265e55138f2d17e8b4c712027009388c18724bd0032f4d57c068c255281703ba6d449345e2f6240a4b8b3047209ff2aca5edec0ad2037403376f5789c4d7c755e4a0b45541a88cd045c77fe61a615e9b6ab6af9e97bbd868593f2b973cd492439c4fdbc6351739e8e9e7f5ba501fb9bbc96605dcf144ebb58f0cc89c528e119788c521bbfa8dd33c69129c1a05ed454d7534224c9df35007ef11b38769ee8d2788c72745e50e14e32b0c2b9d988e2550dd3f89cbd6b5"
"kfrag": "0bb747aeafa005ceb7be0d1f7fac93094150c5d1c4a08b7705dfe98d38a901ddc7eb5aad9d622148f2c229f53d46b8a17dab61b37eb19bdd6c062fa81d725c93036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b038ab4abc7c62834517b7a296199e43c62da3dfa6c4ecadb4a3aa7aac5f4d9f301cdaacf4115acea35edd9858ee8a04a05cbcf346374ca4a3daa54da89754e018222f869edbd7224f49fd705b53cd11af90b078d77b58b272e1e39a10be6a9966db3d3ba5ba267c360b96cf2aaeb28e87f87f76f7216af4e1594b67f71e8ba195f4c305d98fe469e738caf771745f3865ebf7fd114efc09f8b69383bae8020ae2f0101",
"cfrag": "023c4dada5a0b7c98e1c11e6f9c3b799cd82f0aaa690884f9249267fc6651be2430362037688c3de037562c09246afef492b8884c3ab75e3ce1c5faa4c5dda7c42450bb747aeafa005ceb7be0d1f7fac93094150c5d1c4a08b7705dfe98d38a901dd036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b03996e89a45de805aa37dbfa3b152e2021777a760fdfe72f0d401daa7506b56fa503e7c4198f54d702d66c1c042fbda485b93f5bb9c815d7dcb9f2a84f36834f82b5038ab4abc7c62834517b7a296199e43c62da3dfa6c4ecadb4a3aa7aac5f4d9f301031f4d53675c072150ad5d10251ec10b70cf84faa58a93357e426050d7ed23be5fa3fa015b98b321d5dc05d92cbd3aea001d408bfc4121ed9f8d2d13d29c5e94acb3d3ba5ba267c360b96cf2aaeb28e87f87f76f7216af4e1594b67f71e8ba195f4c305d98fe469e738caf771745f3865ebf7fd114efc09f8b69383bae8020ae2f"
},
{
"kfrag": "afdbd2183491cec86259c4e6785c9048f17e1f0c86df597983e78f50eb9e88ebfc725b6addb07ce67e5e33e404237dfd9baf15825329d780bbcf07952d899709025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb74902fbba98644ea41f7abbef7f0a80d645d428844eb6a08c848b73122057f02cc264f27b1b019d667b1a3c89e176b9203870c42dcf3bb6af9191a89db98b2cd1a1b82c61d188f2d2a5d8e1c92a64d3402e98ca09e4594dd67ad74a9583e47b5ee2fd0d38772f8dee198e6e7fa2f7acca079a90b280f98e880106bc337c314bdea20b1e8d1d43ed0912a4a55475c2587ba81a16b9775681774a8bc9d1af2395875e000101",
"cfrag": "02b6b320eb5ef6bb58adc6b0379b3052d7fe3ce290d62d8a9edc5d9560c7472f75036dd5f864310b2f9f7239bca239877edf63b42ac00637fb6cdcdcf944fb0ef2f2afdbd2183491cec86259c4e6785c9048f17e1f0c86df597983e78f50eb9e88eb025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb749037b55bdc05a83cf07d672c695d0c57a961be2a7289d9386b6e86953d6abe90f17036e5e32b140cf076873542eb4551c328608c8f2d951a3d52083b7a2f6612c21d902fbba98644ea41f7abbef7f0a80d645d428844eb6a08c848b73122057f02cc264027ddbf0d8eec3118c1765eb0c636184375b5ce593c1fd83bfbf501d94687695954edccdff64dd78e9ae94aa137754c502c03cacbaeeb09b1dcb798bb8442476ba0d38772f8dee198e6e7fa2f7acca079a90b280f98e880106bc337c314bdea20b1e8d1d43ed0912a4a55475c2587ba81a16b9775681774a8bc9d1af2395875e00"
"kfrag": "4ace898ce8231afe709556f0816b7c3b18a9aaa50fd6afe4596cc32b5cad26a68634f36bf1374f4a915477f9cdb0b8ae2ad6d4b2fc149c693a2cef1a248452c5036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b027c2de1ca923a4f1c7783d42dd0d145efeb1fb8b352168a255ec4661a7ad5532b9653705171ebc24147bb8c9f97500b25f2a3a5304e3d8dfc91d2a1f8dc0d4a5e29044bc078fbe2d8c9b6e500a70af07fbce661f4ecb86cdb2c847b11c136782100962b6d129d9cdb3fa930980c8fe7326fc0897e5a40cdf01f2984fa017c19fb399a0157286cd368653d27522d8775557009377051c9b56b69b014d051ae8b1a0101",
"cfrag": "033c0f532b1e80753d70dc8088fec6132412308e4b27503875878f47475451169b03bd063c6a2189baf14c826a187b2b4077e536775504d0b302e4006f5180f92c284ace898ce8231afe709556f0816b7c3b18a9aaa50fd6afe4596cc32b5cad26a6036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b03f5b99feb5baf4c4aa52bab260270028e11cbcaadfafc740a078e222da31e6cea038348e03d9cb890cdd87ff58b041f1b08724706785352950c321173471eeb917b027c2de1ca923a4f1c7783d42dd0d145efeb1fb8b352168a255ec4661a7ad5532b02bcc145a4a94674c87de586a97eb1b3255ec3e07184916495243f73c71b106c8132071b0325743caec3d1e264ddcc9a8dfca5e7a971ae00c4d79112ea5460787000962b6d129d9cdb3fa930980c8fe7326fc0897e5a40cdf01f2984fa017c19fb399a0157286cd368653d27522d8775557009377051c9b56b69b014d051ae8b1a"
},
{
"kfrag": "831f7b7b14181746fcfd7e26e03e3fb63e7a8903acff3dd3c7ad0c2c94550d9abcced3aacb62fa59fd2847a2b18cc6a6f0a6f646e39ec94f05ef186186dfbe10025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb749020db8647602d7401b47c556f25540bd45da99e69462ee8eef1b9c26a26a3757c77f90b015143d8516a5bf2ada51a24b8192e1326d38bf47c0f54591619db42c7c122fd0d955f788dfdeee8d762022887216a3de332632d44092f1f06793eb351fd6acaf0f2060ace737a11e1c1cec72ba361ce754c913765113e1695de323225f24c3f0bbf832c9062d004b69e40090930b40d5986eb9ede462e5d044083127450101",
"cfrag": "038a54e78c4dd408df7b242610d5c21bc6a34f7b16af21a059ee8e44eea24337c702d4584beebc883104f375e922aba70ad1449d54f6a835ba6716b36271bfae8e53831f7b7b14181746fcfd7e26e03e3fb63e7a8903acff3dd3c7ad0c2c94550d9a025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb74903c1d11486753d8d4b60a4e8a890007d021ea00aaed7d608b25475e69364e5f3120304ef459543d9482aa3b48c5605176e87bf55c0bef3f03244bac83446dd84fe3d020db8647602d7401b47c556f25540bd45da99e69462ee8eef1b9c26a26a3757c702182da47b2bef272ed9840eb2cff17f8f62d3e9544cfc699905e40e8ab061d4d17dc4e840d39308e81fae434b30ed124ce08f846a1a9ab73c926a6a222d15657ad6acaf0f2060ace737a11e1c1cec72ba361ce754c913765113e1695de323225f24c3f0bbf832c9062d004b69e40090930b40d5986eb9ede462e5d04408312745"
"kfrag": "ccc346110f4ab3fa3b0aeca1630e077537ac5e2c74f31a6d09de1a74e5db4f044a1e816cd104fdfabc92bbb40c6f9361071260c13a14b866097749062af79d83036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b03c18e0774891c9d7bb8502e35a2dc2aabeb461f4f0cf43a78a75786869007d60895a31ea32286ff5516b3510559e1e7e3bbbebcedd1c7540b002764e6901816244a073e19a1d9c5e460fbec5dbd33717e9958c89f6adab0484cbd40d1163b8ea71a7208d0de602d4d12c024c0974568fc2aaf23aafb1405157cbcf97738fc9dc0076b9f77d53e20bab18afba1ef40232b55335dbf33ccc365df277fe1a8ecaa530101",
"cfrag": "02a6dd1684794a82183794970974fffc79009ddd99d67a8e4ccf331b66b52b74ed0212f0c112b1e35a9bed7e7c623ae0330010b02a7e1a273b93e4f1fb1ef994dc98ccc346110f4ab3fa3b0aeca1630e077537ac5e2c74f31a6d09de1a74e5db4f04036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b025b53b9f0a4881f2a830fa0a5b851eb616580aba7fb111348183031527a2476aa039ae97d9f19a6b4dbdf1f413f1eb986b5b5deb31f3fccf18ff6c160106064140403c18e0774891c9d7bb8502e35a2dc2aabeb461f4f0cf43a78a75786869007d608024ddf944ec195e26813a514b9a700a27ab4c5ae341b49fff234c61c7d3f0f3a47eccb3bc2212c779b4df0685192f77b57cb25f9849baee3f8c7f7849eeca622d41a7208d0de602d4d12c024c0974568fc2aaf23aafb1405157cbcf97738fc9dc0076b9f77d53e20bab18afba1ef40232b55335dbf33ccc365df277fe1a8ecaa53"
},
{
"kfrag": "c63acdf5fb6820df50354e74e65eefacaf75e7e22c780b2b5e2c49a0db9d353e7217ca6a942fce879bd4b596aabd489e0c5af75bd94b72aed37999799e9c8dc9025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb74902fcaa0a22aabe3b4564972a9368d6a752bc5e359b306cfaff1626b49911158cda599e662db88909f430575f44b3677a8161a5003519e48cf0da635cb20b15f18d632a1bf41eb525b9ee9fd47f196fdd15059d989f9166e5b437cbbd66459e03fad404248f1da8048a5bf9dd9650fbdd50d1afcbadabbcd7a5aca1882b5eeb803443cd4caff8c4b090bc263f56f496aca086bb6f1fedfd5a99e1715ebfe2c6b5010101",
"cfrag": "038caa0566b556fb81617617ee8d4a4808cfaac1fa65b1e1999b58bbefafb30623022a43ae7cd2595e155f3cc7cf5e716168f2b959d574e07f7037ba30329dc384dec63acdf5fb6820df50354e74e65eefacaf75e7e22c780b2b5e2c49a0db9d353e025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb74903361957fd737be52d9423a400e677264d8e57ffc1ab35f19aff29ac7d13275ece03a1b57ec36f35089242d2586be843853ce830f1fff20cea9e6a354598ec10c1e002fcaa0a22aabe3b4564972a9368d6a752bc5e359b306cfaff1626b49911158cda0300a120a88ee5994de9a4a5287dbb2e63bd1cd3fbca77740ddad0789dc4a40547a792377ec089eadb1771196797c6835cf01bd4307c6c33be4e61acdb6e05f583d404248f1da8048a5bf9dd9650fbdd50d1afcbadabbcd7a5aca1882b5eeb803443cd4caff8c4b090bc263f56f496aca086bb6f1fedfd5a99e1715ebfe2c6b501"
"kfrag": "3166d84565cc545cd994957f2aee5009edf3611b5b348e6d39eb27c02198f3cf722cd82f01fd45953cc999741fedf34ea363a229b4414340dbacd0a3fbefb13c036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b02e307917315eab38d97ba09a5fda8c278b4678ad9c8486b17b79739fdd2c9b088ff59ee77f0f2a5455e4d31439fe3652a284839c2c0fd1e989b5f80179501873b13b84ac39b7f9f0de069eedfa3c79d4781dfcceba1c344e0f003f4053ba938465b0e56d5066adea34ff1365e77358dc2a44a319d9a0a7edc283d05790cf9a8152bfab400aefd27bd41374b761de99974a94c4d1076d78269cadb4011cb06b1180101",
"cfrag": "02af56a5bad7fef91e011ac17e1d2066842be5b0fca15cbe1b6ac561281756b4ee02ff6d03924099b91ad03484b4eed47c1559f9869c239d7a42edefd2622a74c9e63166d84565cc545cd994957f2aee5009edf3611b5b348e6d39eb27c02198f3cf036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b038744e4145d2a32bdca6c10199087b720891c9d8510516eb13f6d4a19ee304c5902c9e81da5bc322c674740a9ffb2ea36238628911aa0420fb87fdc53587cc8129c02e307917315eab38d97ba09a5fda8c278b4678ad9c8486b17b79739fdd2c9b08803bd8be6d162bd571a59d0980694944b7e1bf315b6fae53413bf3c1317ad25df26bf5c8477011a67963c77b4eda856b0128d32d58a1be455b5956b93b7ff2a8c585b0e56d5066adea34ff1365e77358dc2a44a319d9a0a7edc283d05790cf9a8152bfab400aefd27bd41374b761de99974a94c4d1076d78269cadb4011cb06b118"
},
{
"kfrag": "1365f76b3cecb0d27cd839263d6e91e8a3e703cb10590fc4ee8c533caed2fcb4e390938ab2eb832d51d7436058182fb46fb4288c9409a2465c25e447919e61eb025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb74903ffe0345c282a9890aa6759dadd14afdd50a82c19123a110d37f4582a1549ce5d4308de0dbbc76897e910253bd1fdb10e65bbbe45cede7dcbf494eba5dee18f9c141cc095ff275208485abe81b229b730c11a1fd87de00756e3d705faff9e6f508cff3f81aec66079b4737c5d55ada03f7a749da2605e6b1bb57324528c373cac773526c02a0cf876744a5e7cd7dee1bad8ccf1e81a46d4c7049743cabca94dad0101",
"cfrag": "03e39b8d144411b2db704a4394b9291bc8897ce619df95e2a8355066a9da8fe91b02037fe5e553f4e7e711502abe59bff33fae5ed0eee63747252744701ad1c550f31365f76b3cecb0d27cd839263d6e91e8a3e703cb10590fc4ee8c533caed2fcb4025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb74903a0bf927bc8b75413f899b06ec3bb3ebba8b73f2a5d22c007e157440c2ec6496703be800d325be2e0d10d3cf81d9fe65e035a31d9f4b857b81af38d52d1a815a40b03ffe0345c282a9890aa6759dadd14afdd50a82c19123a110d37f4582a1549ce5d02518eea3159155d5ec2437907ae06a3bf0ece2e8747e13c1528ccffc62eedeebeb50690a4f2f1e5ebcec48789e7d08039ae4853de86a2a2fd991e70ba37e7dc1c8cff3f81aec66079b4737c5d55ada03f7a749da2605e6b1bb57324528c373cac773526c02a0cf876744a5e7cd7dee1bad8ccf1e81a46d4c7049743cabca94dad"
"kfrag": "71c8673926b3df970da991a3d53cd6cff27963ac7d88ad2921e84ae47e8f3c930112609fb5f87ef70ebdbdec2d9cc58fd1f171bea4b93fb4a57de058666fdcc5036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b033c83e00f04744ed4ac3a6cadc64568dfe526e579f56f5c26e5749a0fe795b0339700c611b793210edfdc823f5332ed4640f6cf09e419336f3e13b71b29de065f7cd643768ebea6137a612bfb8328cb0868ac6e001e9bf8270817446b15027a1d6e6b066214a61c379b54d71b608774172181d09ff6b14ab43dae3ad68fb18abe2fad7b818fe6bee1c27158d5bc3c85c114611c43bfe52e4f6969b9a53c7db1240101",
"cfrag": "0249811dd5bab624b9501be1cfff695d94b44f80d2aea9a107fdaa6e0fac70784c0370888b3ceed890b616afe29cd580fc0808b5f4fb5dde04f6e91382a39e1fef1671c8673926b3df970da991a3d53cd6cff27963ac7d88ad2921e84ae47e8f3c93036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b0240fdfd21499d29990d5c44133fe7ad4597337f7bc18d7768ffc50f6a5661d14903dc2ab1e56a45fa98de15f0c32d46adf0ae39884736ea884a1dd1f4c4c7e8669d033c83e00f04744ed4ac3a6cadc64568dfe526e579f56f5c26e5749a0fe795b03303d4d12caf9a7337897a9f0bf47a232c26347a78eb4e8920900a692d9c87ce13805c978b4b22177a905407452d108a654e28ddce1d9480265bfee554587a6fc7ac6e6b066214a61c379b54d71b608774172181d09ff6b14ab43dae3ad68fb18abe2fad7b818fe6bee1c27158d5bc3c85c114611c43bfe52e4f6969b9a53c7db124"
},
{
"kfrag": "d961d60a31b197ee26437b1ff6b484c63bcfd208f509af1225a8db8f65240e366f7924ae66d6cd64c25c74761067bcb3a388cf2d4d2e90738666252868e790b0025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb749037c88f9bf80bb6a406843e339dcc8548b3f8bf16e525be9c94d5e3b8c243f466fbb19cf3eec376f9c46d9c52040555927c7eb506af29718ed9b728ac210d43b2b4958eae22852a1e3761a71cedff210a52f5d312e5cc1b12bdbb2edbe3f955f5535b8b1ce9e3e8b04a010a2dd227c9d906d7806fdf9df07d8ce379597bf7513c23db95d84e7774a3fe0de392cd7390c6ec7cab60ad22317d8c00de0865bd0ceb80101",
"cfrag": "03367a497f07271e9b505f58d762527cfd8b0bd30f4b925219b495c8ff825c8cef023b856c54ee517300280f9b9109ee6f8e260341c4013a0b74911fda9eac562b71d961d60a31b197ee26437b1ff6b484c63bcfd208f509af1225a8db8f65240e36025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb74902d5dd9d1d29e06800648c0b2b45a2755f5a36ed4e7179d17c5ed9506d1d455daf02fe8deea5266b7059e6bdf53cb4f23c3f27814e14f955eb6236ca911639c3d6f8037c88f9bf80bb6a406843e339dcc8548b3f8bf16e525be9c94d5e3b8c243f466f03bdd9ce48dbeafe54277c44c4cf3cb5d5eb10e036640180c0d0e197594f2bebe029f79a6eafcac19331d83eab043df3aa6486a70409c46c2509a4b03ead49666b35b8b1ce9e3e8b04a010a2dd227c9d906d7806fdf9df07d8ce379597bf7513c23db95d84e7774a3fe0de392cd7390c6ec7cab60ad22317d8c00de0865bd0ceb8"
"kfrag": "4e27b679bb415c4fb50a84e342b7a6405c03b015199f8178d825be10c304976bcb91e3038cf7f69cf437fab1a1d55a7ae61710248d1cd45ce31055492c9f9d2c036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b024f2493f41095fec4b28b7dc3d9a91ce656ac98f23f35e4ff0e87d27e422bb445a01e102936f9c672c72adf3dd9a659875b93a8a4fa5ce35dc27fae52e3b7274c110a3cf0dfc988a3dfeda1621831686c00c7c164747a484f3f254ffd05a453f7b84772259002ac4555054edfc31a58a597ef47e8dcbca0860acc2668d646d5411ff0e2b9f5bfe651197a66927dbd57a973d7b18c1e1ffc0ad29c6ccb9c07698e0101",
"cfrag": "02e1d44d51eb0b901c9e7ae75012365fa614ac37352c39abcf5111503f1fce8a0a02265e4caa6bb349c83409ceb4d697e10755051426a60f64455d427dd2d13b4c754e27b679bb415c4fb50a84e342b7a6405c03b015199f8178d825be10c304976b036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b02f70caa9664f731c6a4e510f366ec1cb30cf8f99bd90064f60e1150a97bdcf71402bcf647a3765a1bd64178900716f9a8c699d20a08fb0768227a377a0bf103874e024f2493f41095fec4b28b7dc3d9a91ce656ac98f23f35e4ff0e87d27e422bb4450221b9645b560128d07ff5ad6cabc8c960655563552eac678f237ffbdfa52031043e921e438182a1cb952ef26c3efc71457653345c3e646274556b8d4f588e3ed2b84772259002ac4555054edfc31a58a597ef47e8dcbca0860acc2668d646d5411ff0e2b9f5bfe651197a66927dbd57a973d7b18c1e1ffc0ad29c6ccb9c07698e"
},
{
"kfrag": "8618ade86dd8b8f15c206274caba6955c30171e466e220ef2344af25f3ca5976ea2eb761d4e8d083e4bf30052c90f6fde525716bd19b5abe98d66bdd48745f85025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb74902bb441051315ea81c8d9d9cc4e63d3f66e6fe3ef52217e8639e0af421d5dbba1be604f5168408cc4bf118346126b9a105b57524b741e141d5cebca15c8964669858bed28994196a23e4f99706cdb8d912d9c53b7c58f98e6d5eb5a4ef3aa2b7b9e0a270af089c667647613c6abe1b7aba8103ee386a04df80341e51e132d6d35d28753fa65e7f8d8c8f3a10d8adc8499a174bcb8041cf6078520ef5e9e00853290101",
"cfrag": "03ae6db34ac0125757de78dc64cb563d32ef5ce3983b67f8053cbb9e3539f5240c03855d0862b9930d7fa36e5c60785352de497b84e6ca6d1712627a35fa49df79688618ade86dd8b8f15c206274caba6955c30171e466e220ef2344af25f3ca5976025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb749021594688234f9a0ba6331cf6d341dcea4992d0354492247c528a011620fe7271a03ebb8652e960aa91511db011d16dc3f15df71d81b6e5555a41dc29480158fd8e302bb441051315ea81c8d9d9cc4e63d3f66e6fe3ef52217e8639e0af421d5dbba1b0398e26bb2caa42e1cc1f55f3688bfcaaadc7714a9f79f1910f5c158faa0d53b370668c081a5622181957a2af1fed562695ec390b01b8855411518f401638e185de0a270af089c667647613c6abe1b7aba8103ee386a04df80341e51e132d6d35d28753fa65e7f8d8c8f3a10d8adc8499a174bcb8041cf6078520ef5e9e0085329"
"kfrag": "7a359c5e31c6bf94bfc09eaa7c199ccda576fdda37d74360d19ddf71930e9423ffb95215a64396ac14f860092c15a7079d58c725484016226a85967c55166602036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b02417fb0a631dc12df3aa4a94c5f838bb2a08aee9c98efae3d5dec6f10d8addd579a3d0eedb2d7826fb6bb8a9fe9b17696ecffd03070afec2f12a315d1dc344470300b9d4641d4cb30760463639a8949444fe5fc08a44d4f36e15640d7945020e6f372b8fce1db831621f1dc7049c5d63140c95dd9ed48de402d09e0eb9a96afbd4a8e539d507cb863bbd5b4cf939271809e4850bbcfb5ca1ff3fef7c377f433490101",
"cfrag": "02b3090b199b3e6d2d179e349c924e934d697b76e1a1dccce61e4c7f463282c7fa037a7f8b1433f7826def664c0ce880fd4f696887e70e29717fa734fe48f07c5b5d7a359c5e31c6bf94bfc09eaa7c199ccda576fdda37d74360d19ddf71930e9423036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b03f1ecde0dec752cf8fbb3ff58adf5504b2f97faf3cf15696038335b7e91b0d11e037548274cdb2390bb55b4ee3c0b17b3691e293bb7ec6f4fa093cb567868954eda02417fb0a631dc12df3aa4a94c5f838bb2a08aee9c98efae3d5dec6f10d8addd5702369696156202061fce67ab68d0f037dca93bd18ad31161b31d80421d541fafad37608b9329e45b9fba642ff5aed153ee1813454febedcc1e92e978172afed57bf372b8fce1db831621f1dc7049c5d63140c95dd9ed48de402d09e0eb9a96afbd4a8e539d507cb863bbd5b4cf939271809e4850bbcfb5ca1ff3fef7c377f43349"
},
{
"kfrag": "939cfdfc368ae2c011ade7b7d18543f66f6b2f5aea0ed6732830d8ab281ec492cd90cd1756a44414e51d741e99f9a7d46cbedbfc7bdb60f6b8d33ffb1d4d6b31025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb749020d6a191832eed71367b495a514ee4ac442ce19040c00c4e6ab331f844b2fff11d1db98034e5ff8f86a5b2d015cfa439774dd83644adb80bcba1881f09bdb1b105e37329495d03775e99da0f1c99e753e6868fbe04766572a086ce8e1c575e0d253698578672fbfdcdf2607513d0d0c03f346cb8183506d179e9b999988c7ca317c10e8606e3f7a18ac9146dcc2f484dc6c46a149c371c7a22b17c14f0e3e84220101",
"cfrag": "03b45ef82c8e3439a698e3cc4383cd223d6ae84a6c1c7e6ea6b1e89fb501273ef1024e2b1a83a2e675e7f3e4f1d803f456dc83d6536bc33c07b01fc521380cec39a4939cfdfc368ae2c011ade7b7d18543f66f6b2f5aea0ed6732830d8ab281ec492025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb74903893bc5023c9da3e0acf0028256ae37fe1015e09b8fcaea89b3aff10a49b1ac5d03c295e648f0dc3df65314ce141ff21b04fcf53432300ff8994ba765d8205bd90d020d6a191832eed71367b495a514ee4ac442ce19040c00c4e6ab331f844b2fff110390d4511e87f7dee7091cbffb952da38c7efbf1ee5a6890ea8afcfa22ee425c0cecd183c8351af7f917ccaa9bd7771de0ef9cf240bc02a7dc32c10e0bd4ed725653698578672fbfdcdf2607513d0d0c03f346cb8183506d179e9b999988c7ca317c10e8606e3f7a18ac9146dcc2f484dc6c46a149c371c7a22b17c14f0e3e8422"
"kfrag": "df55683a69e4136205a61c895959d86eb64840bb2175d5d15e4491b27e6a38bc80dc89e8f1631175607e6c67a34a758020c74693337cf3714a6cf78d355da17a036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b03ab6ffe8768ecd246956362065ca48488183f96c3ee5957be7a7ce02f3ef9cb12bab3cf7756f986806106919c948a4b191bbf84df94503a2b0a81a5141cb9e1e17de0ee2599d0bbafe972bd9e567eece93c262fc634ed2690a1914abda632fac2ae3cc293a58554a55dbc50c0e111c6e9bb0208631833da5e66b1adb2aa1ecdd615c09098fba7af4b03763b846e1f5358a6bac27c36eadd6cd2c98fd58c277e0b0101",
"cfrag": "03a3e0b6b2fa9f634acac1d4ed682dbf0c08ba27889087854059d80d021797d7b403a64ced5bc32ad0cf5746eb5ab1ebdc12a1d1bbbd0f45d0fc90c3e4b56ba70546df55683a69e4136205a61c895959d86eb64840bb2175d5d15e4491b27e6a38bc036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b02ef471b06988f07ce34c7bfa5825603eedbde4138c05296055cff54f6204d245a02119417e7aa9c207a71540abefb8de8e04c977db7d11a82e2f98a1ea3fff5519e03ab6ffe8768ecd246956362065ca48488183f96c3ee5957be7a7ce02f3ef9cb1202e8febd066a999b83b4441662636c6c978cb8789553914b5ed94c02d4784432d74e9fffcb5ce10b65d95624b23940415f0dbe29e174a6b73c78cb3d5d380d8882ae3cc293a58554a55dbc50c0e111c6e9bb0208631833da5e66b1adb2aa1ecdd615c09098fba7af4b03763b846e1f5358a6bac27c36eadd6cd2c98fd58c277e0b"
}
]
}

View File

@ -2,39 +2,39 @@
"name": "Test vectors for KFrags",
"description": "This is a collection of KFrags generated under the enclosed delegating, verifying and receiving keys. Each of them must deserialize correctly and the call to verify() must succeed.",
"params": "default",
"verifying_pk": "03f24761ac8b02de08ad1622d023f669d6214c3bab81a33087ed3ec5505e4d43db",
"delegating_pk": "03a73623a2e72fd52b2d313214c7495580c14fe6cd8de7ad0d63bbfbfd6fb6bd4d",
"receiving_pk": "02952a1903b9c929f0d93d935b34b272ea25a84833a04e22d887f27bc3bf0cc409",
"verifying_pk": "030b95b3f249297824b32d3391392d62a9aff32e8698fa78c7e8ce4a9d17071f56",
"delegating_pk": "02d67029bb92522059225d190038230c23466e28d132d48f714f9098168a562b8a",
"receiving_pk": "03b0d0243e8954b408047eee3b09b5ed132ccc25ec70e99fc74b6e9f54e5ecf9c7",
"vectors": [
{
"kfrag": "3f3453856117dedb3d7518d0435c04b10734700c8aca48fa5a8b85eded515cdffefbefddcd3cb728dcc6d05061b5e201aa1cef55ce89e94c34b6887b307d5ab2025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb7490374d2e59ea274c8011f6bf26ca8fe8eb8e7837cafed8547485e3fde6ffe0368e98d26b0d7e12095941daffae8ca429147d9686dc3285ea182c0d7b15db41c7afd3fae3c88100707ef28b53d7cf93961dc509864eb319f0cce274544306c96099b5792af0b820bf758f5c8f2a69cfc1e0a5f91fab96ab82c10c7740602f90efdd315ff5dbd07323526ca1c9a93132b4d008fdf1796a55b92e2fe75c544652256c80101"
"kfrag": "2565903141941fecb01fb92dfbdd66a0a1d4eb94d3226beb2f7adcbb8282ffd0b7934320225868d8f9ace7784fd3ae0c20a09c5a2b698fae0554d71b1f016670036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b026cb94f302809d19aacc81da19f2156db9c610498310b930d7787d8a2366dadd3d977c6befcff3088016d5d9050f4932cb4a9ce483dd7cf27e1f157f8d23f598248d5027a8961a09c874c82a8c479fbf074675790edff37e6927a7d4ea269e600685dc9469c87e6d9d469656b2f40b33e6d6813ec3519fc065a4ffc0eeb8abd324c1914451341cdb7abd7c5aa01adade220b2894be5a33caa9ee7d3b4637bdccf0101"
},
{
"kfrag": "d15ac028be9938b5e2d7a9fb9957b416db538a11d141d320a5f656ab4e3cb0cab3a2ddd2672f3514e7da2f29cbe3815c83b6d704e09d595a68fdd5aeff52ef8a025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb749033d6984555d41614a2ba08c2182e27a7105cb60ed414c9732a156c3e44d196dd3507335e3781d6c9a4543e3ae81a4c7538cf280292c9c0d92e4f513e4e073721f2f72a1ea0e69912e21754e32edf9cefd3a96183f5501266bedb301f291709ffb25ed5213660db4c8bfc086e794650455bfde1251f92e1fe49f1feb016ad44fe859b342964db14dddfa6a33eba53021149d8663d306850b8907f9dabf4a5d3ecd0101"
"kfrag": "ae85e679cf44b1877ff36e48c4965144724f17cc7605f53e50007aabbc2c7ab3871d04e6b5666b81a99ba0c6a70ba8675f2122a39eaec04a62c8496d5149ac9d036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b02e3e7722bffcb6674987b0089f6b8ac0d6cb220d9661edb5ed9dca5fcd33d5bdb1e41b92c9a8e4346b2dd1072389e4ce36a6230eecb59b7e0e9d84f35bf14f0f23014a1263e17d07a94c5d9c248c692686c745df3c5d11566b9ca17ec62e59a5265749ef2701a58483bd106be985c3e4a03750e3c0c21424cf7f59682224c4f6e29c242904ade5ac377974eaa662b6ed6eed13fd30509b9cb332b4fa89043a2a70101"
},
{
"kfrag": "f780e25b78581e70d28f102264e1f04b482eb9ecd45c188f2f9cd90026627f904b903c6da7529805e1d91fbaebc384588083e2c5f235c31e7f768b8d0df43513025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb749032f4d57c068c255281703ba6d449345e2f6240a4b8b3047209ff2aca5edec0ad2394020e796b7b9f9130c0ab02b4c8151d050f38f3d0ff5f38dc164e4d254ad2737758c4b67c0b54267b113692f11a410ffc5fe3e41c2f8ec9246854baa63dbfaebb58f0cc89c528e119788c521bbfa8dd33c69129c1a05ed454d7534224c9df35007ef11b38769ee8d2788c72745e50e14e32b0c2b9d988e2550dd3f89cbd6b50101"
"kfrag": "0bb747aeafa005ceb7be0d1f7fac93094150c5d1c4a08b7705dfe98d38a901ddc7eb5aad9d622148f2c229f53d46b8a17dab61b37eb19bdd6c062fa81d725c93036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b038ab4abc7c62834517b7a296199e43c62da3dfa6c4ecadb4a3aa7aac5f4d9f301cdaacf4115acea35edd9858ee8a04a05cbcf346374ca4a3daa54da89754e018222f869edbd7224f49fd705b53cd11af90b078d77b58b272e1e39a10be6a9966db3d3ba5ba267c360b96cf2aaeb28e87f87f76f7216af4e1594b67f71e8ba195f4c305d98fe469e738caf771745f3865ebf7fd114efc09f8b69383bae8020ae2f0101"
},
{
"kfrag": "afdbd2183491cec86259c4e6785c9048f17e1f0c86df597983e78f50eb9e88ebfc725b6addb07ce67e5e33e404237dfd9baf15825329d780bbcf07952d899709025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb74902fbba98644ea41f7abbef7f0a80d645d428844eb6a08c848b73122057f02cc264f27b1b019d667b1a3c89e176b9203870c42dcf3bb6af9191a89db98b2cd1a1b82c61d188f2d2a5d8e1c92a64d3402e98ca09e4594dd67ad74a9583e47b5ee2fd0d38772f8dee198e6e7fa2f7acca079a90b280f98e880106bc337c314bdea20b1e8d1d43ed0912a4a55475c2587ba81a16b9775681774a8bc9d1af2395875e000101"
"kfrag": "4ace898ce8231afe709556f0816b7c3b18a9aaa50fd6afe4596cc32b5cad26a68634f36bf1374f4a915477f9cdb0b8ae2ad6d4b2fc149c693a2cef1a248452c5036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b027c2de1ca923a4f1c7783d42dd0d145efeb1fb8b352168a255ec4661a7ad5532b9653705171ebc24147bb8c9f97500b25f2a3a5304e3d8dfc91d2a1f8dc0d4a5e29044bc078fbe2d8c9b6e500a70af07fbce661f4ecb86cdb2c847b11c136782100962b6d129d9cdb3fa930980c8fe7326fc0897e5a40cdf01f2984fa017c19fb399a0157286cd368653d27522d8775557009377051c9b56b69b014d051ae8b1a0101"
},
{
"kfrag": "831f7b7b14181746fcfd7e26e03e3fb63e7a8903acff3dd3c7ad0c2c94550d9abcced3aacb62fa59fd2847a2b18cc6a6f0a6f646e39ec94f05ef186186dfbe10025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb749020db8647602d7401b47c556f25540bd45da99e69462ee8eef1b9c26a26a3757c77f90b015143d8516a5bf2ada51a24b8192e1326d38bf47c0f54591619db42c7c122fd0d955f788dfdeee8d762022887216a3de332632d44092f1f06793eb351fd6acaf0f2060ace737a11e1c1cec72ba361ce754c913765113e1695de323225f24c3f0bbf832c9062d004b69e40090930b40d5986eb9ede462e5d044083127450101"
"kfrag": "ccc346110f4ab3fa3b0aeca1630e077537ac5e2c74f31a6d09de1a74e5db4f044a1e816cd104fdfabc92bbb40c6f9361071260c13a14b866097749062af79d83036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b03c18e0774891c9d7bb8502e35a2dc2aabeb461f4f0cf43a78a75786869007d60895a31ea32286ff5516b3510559e1e7e3bbbebcedd1c7540b002764e6901816244a073e19a1d9c5e460fbec5dbd33717e9958c89f6adab0484cbd40d1163b8ea71a7208d0de602d4d12c024c0974568fc2aaf23aafb1405157cbcf97738fc9dc0076b9f77d53e20bab18afba1ef40232b55335dbf33ccc365df277fe1a8ecaa530101"
},
{
"kfrag": "c63acdf5fb6820df50354e74e65eefacaf75e7e22c780b2b5e2c49a0db9d353e7217ca6a942fce879bd4b596aabd489e0c5af75bd94b72aed37999799e9c8dc9025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb74902fcaa0a22aabe3b4564972a9368d6a752bc5e359b306cfaff1626b49911158cda599e662db88909f430575f44b3677a8161a5003519e48cf0da635cb20b15f18d632a1bf41eb525b9ee9fd47f196fdd15059d989f9166e5b437cbbd66459e03fad404248f1da8048a5bf9dd9650fbdd50d1afcbadabbcd7a5aca1882b5eeb803443cd4caff8c4b090bc263f56f496aca086bb6f1fedfd5a99e1715ebfe2c6b5010101"
"kfrag": "3166d84565cc545cd994957f2aee5009edf3611b5b348e6d39eb27c02198f3cf722cd82f01fd45953cc999741fedf34ea363a229b4414340dbacd0a3fbefb13c036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b02e307917315eab38d97ba09a5fda8c278b4678ad9c8486b17b79739fdd2c9b088ff59ee77f0f2a5455e4d31439fe3652a284839c2c0fd1e989b5f80179501873b13b84ac39b7f9f0de069eedfa3c79d4781dfcceba1c344e0f003f4053ba938465b0e56d5066adea34ff1365e77358dc2a44a319d9a0a7edc283d05790cf9a8152bfab400aefd27bd41374b761de99974a94c4d1076d78269cadb4011cb06b1180101"
},
{
"kfrag": "1365f76b3cecb0d27cd839263d6e91e8a3e703cb10590fc4ee8c533caed2fcb4e390938ab2eb832d51d7436058182fb46fb4288c9409a2465c25e447919e61eb025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb74903ffe0345c282a9890aa6759dadd14afdd50a82c19123a110d37f4582a1549ce5d4308de0dbbc76897e910253bd1fdb10e65bbbe45cede7dcbf494eba5dee18f9c141cc095ff275208485abe81b229b730c11a1fd87de00756e3d705faff9e6f508cff3f81aec66079b4737c5d55ada03f7a749da2605e6b1bb57324528c373cac773526c02a0cf876744a5e7cd7dee1bad8ccf1e81a46d4c7049743cabca94dad0101"
"kfrag": "71c8673926b3df970da991a3d53cd6cff27963ac7d88ad2921e84ae47e8f3c930112609fb5f87ef70ebdbdec2d9cc58fd1f171bea4b93fb4a57de058666fdcc5036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b033c83e00f04744ed4ac3a6cadc64568dfe526e579f56f5c26e5749a0fe795b0339700c611b793210edfdc823f5332ed4640f6cf09e419336f3e13b71b29de065f7cd643768ebea6137a612bfb8328cb0868ac6e001e9bf8270817446b15027a1d6e6b066214a61c379b54d71b608774172181d09ff6b14ab43dae3ad68fb18abe2fad7b818fe6bee1c27158d5bc3c85c114611c43bfe52e4f6969b9a53c7db1240101"
},
{
"kfrag": "d961d60a31b197ee26437b1ff6b484c63bcfd208f509af1225a8db8f65240e366f7924ae66d6cd64c25c74761067bcb3a388cf2d4d2e90738666252868e790b0025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb749037c88f9bf80bb6a406843e339dcc8548b3f8bf16e525be9c94d5e3b8c243f466fbb19cf3eec376f9c46d9c52040555927c7eb506af29718ed9b728ac210d43b2b4958eae22852a1e3761a71cedff210a52f5d312e5cc1b12bdbb2edbe3f955f5535b8b1ce9e3e8b04a010a2dd227c9d906d7806fdf9df07d8ce379597bf7513c23db95d84e7774a3fe0de392cd7390c6ec7cab60ad22317d8c00de0865bd0ceb80101"
"kfrag": "4e27b679bb415c4fb50a84e342b7a6405c03b015199f8178d825be10c304976bcb91e3038cf7f69cf437fab1a1d55a7ae61710248d1cd45ce31055492c9f9d2c036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b024f2493f41095fec4b28b7dc3d9a91ce656ac98f23f35e4ff0e87d27e422bb445a01e102936f9c672c72adf3dd9a659875b93a8a4fa5ce35dc27fae52e3b7274c110a3cf0dfc988a3dfeda1621831686c00c7c164747a484f3f254ffd05a453f7b84772259002ac4555054edfc31a58a597ef47e8dcbca0860acc2668d646d5411ff0e2b9f5bfe651197a66927dbd57a973d7b18c1e1ffc0ad29c6ccb9c07698e0101"
},
{
"kfrag": "8618ade86dd8b8f15c206274caba6955c30171e466e220ef2344af25f3ca5976ea2eb761d4e8d083e4bf30052c90f6fde525716bd19b5abe98d66bdd48745f85025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb74902bb441051315ea81c8d9d9cc4e63d3f66e6fe3ef52217e8639e0af421d5dbba1be604f5168408cc4bf118346126b9a105b57524b741e141d5cebca15c8964669858bed28994196a23e4f99706cdb8d912d9c53b7c58f98e6d5eb5a4ef3aa2b7b9e0a270af089c667647613c6abe1b7aba8103ee386a04df80341e51e132d6d35d28753fa65e7f8d8c8f3a10d8adc8499a174bcb8041cf6078520ef5e9e00853290101"
"kfrag": "7a359c5e31c6bf94bfc09eaa7c199ccda576fdda37d74360d19ddf71930e9423ffb95215a64396ac14f860092c15a7079d58c725484016226a85967c55166602036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b02417fb0a631dc12df3aa4a94c5f838bb2a08aee9c98efae3d5dec6f10d8addd579a3d0eedb2d7826fb6bb8a9fe9b17696ecffd03070afec2f12a315d1dc344470300b9d4641d4cb30760463639a8949444fe5fc08a44d4f36e15640d7945020e6f372b8fce1db831621f1dc7049c5d63140c95dd9ed48de402d09e0eb9a96afbd4a8e539d507cb863bbd5b4cf939271809e4850bbcfb5ca1ff3fef7c377f433490101"
},
{
"kfrag": "939cfdfc368ae2c011ade7b7d18543f66f6b2f5aea0ed6732830d8ab281ec492cd90cd1756a44414e51d741e99f9a7d46cbedbfc7bdb60f6b8d33ffb1d4d6b31025141c7f166303d33491fb9aae65c941fd7f40e5c9270a9600c20281090dbb749020d6a191832eed71367b495a514ee4ac442ce19040c00c4e6ab331f844b2fff11d1db98034e5ff8f86a5b2d015cfa439774dd83644adb80bcba1881f09bdb1b105e37329495d03775e99da0f1c99e753e6868fbe04766572a086ce8e1c575e0d253698578672fbfdcdf2607513d0d0c03f346cb8183506d179e9b999988c7ca317c10e8606e3f7a18ac9146dcc2f484dc6c46a149c371c7a22b17c14f0e3e84220101"
"kfrag": "df55683a69e4136205a61c895959d86eb64840bb2175d5d15e4491b27e6a38bc80dc89e8f1631175607e6c67a34a758020c74693337cf3714a6cf78d355da17a036523005d1234abcd44d36704620cfcff444edac7a64dfc256281966351a7803b03ab6ffe8768ecd246956362065ca48488183f96c3ee5957be7a7ce02f3ef9cb12bab3cf7756f986806106919c948a4b191bbf84df94503a2b0a81a5141cb9e1e17de0ee2599d0bbafe972bd9e567eece93c262fc634ed2690a1914abda632fac2ae3cc293a58554a55dbc50c0e111c6e9bb0208631833da5e66b1adb2aa1ecdd615c09098fba7af4b03763b846e1f5358a6bac27c36eadd6cd2c98fd58c277e0b0101"
}
]
}

View File

@ -1,37 +1,37 @@
{
"name": "Test vectors for CurvePoint operations",
"params": "default",
"first CurvePoint operand": "02eb0184eedd9d14e10ad0714afd9915c58b2b40b582283e3e741d0141189246c0",
"second CurvePoint operand": "02b925f594ea60040f470195c72fc7aa8caeac7161af3abe65dceb2908bc866754",
"CurveScalar operand": "d63d8806eba7bfc2f5fd77d21aa5d7cc7cffcee26ac096f7c5904629c0db1c12",
"first CurvePoint operand": "03945c237f3b4cab9638d89bba3a098d37a8f4c981788969c2846e988748315bfd",
"second CurvePoint operand": "03d495c93def84d2b7c709d5896d64339129a28db5a6a2cd4c686c895da538228e",
"CurveScalar operand": "0a686fbc44fdc4713b7901c88fe416cc9b187501e671a4c70781742f3bcef1ec",
"vectors": [
{
"operation": "Addition",
"result": "02de2d20749111f538575c81e0abc406c71cf6d4c7c1b3555476d6f8778f271d5a"
"result": "02338b0a976b3701353558685966d2b166174dbce6d697d2e50069833a06157bf9"
},
{
"operation": "Subtraction",
"result": "031bd3e6f5b33b3f94601d3a243efaf2c359c3b097aff1aa3fa8fbf5210f110b8d"
"result": "02864a1ee4df38dffb51940819fca1a89624521ed14b8ec873e4b701fa6d8c1f5b"
},
{
"operation": "Multiplication",
"result": "020fd46d21ec56d94a787c6d717222489b7a070a4f57065f442f588789d1feed87"
"result": "03e7882ab867f7006915d16738ef9adebc2a1946fea829478e7e444e6113ecf1f1"
},
{
"operation": "Inversion",
"result": "03eb0184eedd9d14e10ad0714afd9915c58b2b40b582283e3e741d0141189246c0"
"result": "02945c237f3b4cab9638d89bba3a098d37a8f4c981788969c2846e988748315bfd"
},
{
"operation": "To_affine.X",
"result": "eb0184eedd9d14e10ad0714afd9915c58b2b40b582283e3e741d0141189246c0"
"result": "945c237f3b4cab9638d89bba3a098d37a8f4c981788969c2846e988748315bfd"
},
{
"operation": "To_affine.Y",
"result": "d9bed51d198e8ccd919b54a6eabfed2032cc737d410e0364643716986de89afc"
"result": "fd0576e382f8f0ce0849c72789c3bd2fe2ee453efc606ff8815108e734e088ef"
},
{
"operation": "kdf",
"result": "40b49492ba7924c421dd61ea39bf94ac6566feff43a1ef14e7adc2b9af3f6664"
"result": "26f9fa1e3d2bd1fe3b14ea0a6e1276a214b1f5c59ca01cb3b74a7cb74e327f1c"
}
]
}

View File

@ -24,69 +24,69 @@
"input": [
{
"class": "CurvePoint",
"bytes": "02a03893438c0502dd13818f65c039b2ef4fce33bfed150c6ad166554b4e8a51c2"
"bytes": "02558f1de19a58e73a94e8fbbc6d3b1de2d312d90746ea74cb29f046943bf57871"
}
],
"output": "cd175898869252f3d6c6e77eaed94a72e74410d99534b349f27df2362c35745a"
"output": "5dfe037b1041c4f4d89bed6305061d5d0f7f996f51cab49af958c9e635c47792"
},
{
"input": [
{
"class": "CurveScalar",
"bytes": "5e8601ee29241f263bf49b9999594413f863193fa1b8a985fff981cda7cc9087"
"bytes": "7a44f4f0e25258ed69f205b3770070c557d288c69a3cc453b2a42205d8c1c196"
}
],
"output": "2b83903fedca70169024365a4a5b387536a9ba38bd7b9fa0462f5f932f41a493"
"output": "93134c97fd051748346bf8ee6dc9f3dd920effc2faa81f7c243d0565b10ebe5e"
},
{
"input": [
{
"class": "CurvePoint",
"bytes": "02a03893438c0502dd13818f65c039b2ef4fce33bfed150c6ad166554b4e8a51c2"
"bytes": "02558f1de19a58e73a94e8fbbc6d3b1de2d312d90746ea74cb29f046943bf57871"
},
{
"class": "CurveScalar",
"bytes": "5e8601ee29241f263bf49b9999594413f863193fa1b8a985fff981cda7cc9087"
"bytes": "7a44f4f0e25258ed69f205b3770070c557d288c69a3cc453b2a42205d8c1c196"
}
],
"output": "9f7cea094a5ed29ab2ec83391527db31850f915781c07c0b13853869e9885968"
"output": "3cd3272ccea4e738abe18c100656a6ed2ba30e6e5723c1205641c8ba72aef03c"
},
{
"input": [
{
"class": "CurvePoint",
"bytes": "02a03893438c0502dd13818f65c039b2ef4fce33bfed150c6ad166554b4e8a51c2"
"bytes": "02558f1de19a58e73a94e8fbbc6d3b1de2d312d90746ea74cb29f046943bf57871"
},
{
"class": "CurvePoint",
"bytes": "02ecbc4dc0aed60efa211c7bf0e238593d292a042373b891928bcee459151c2f44"
"bytes": "025e6a08eb9376adfea3a92e05fea213c493fe051461fdf5639a7108e8687eeacf"
},
{
"class": "CurvePoint",
"bytes": "02aa6a09c61286e36d82f6371038f1c33b2095b3b6dc8d09de7489f516c2dfe49a"
"bytes": "030d7ea7752848f5af2aa01bde8b45e180089fc7cdbc60b59235207a6527773d73"
},
{
"class": "CurvePoint",
"bytes": "028da9ebf8cc0966bc010152fd3917a8f12dfff0af3b06e34e17f300d622893159"
"bytes": "02906780e9484aec2102a01a157f10ced5aec952cd00631d94d5ea2edfa9b68083"
},
{
"class": "CurvePoint",
"bytes": "0281171b7e330ebd097575dadb210e8a405bc162e293881457a301da03f7571c7a"
"bytes": "03fcfdb46bf83a68e0d674e7d5b7c0365c8fa05dd418f2ba1a4aea2abcbcd12a19"
},
{
"class": "CurvePoint",
"bytes": "03e2869b26bbf46a2e1f46116d8d9eeeb45f18acf4a808defee52040221dd08af9"
"bytes": "02d45ec4ea9bf9d0acfba0422c6d4cfb087bd2f0084127eb90debdd94e391927f7"
},
{
"class": "CurvePoint",
"bytes": "0374d2e59ea274c8011f6bf26ca8fe8eb8e7837cafed8547485e3fde6ffe0368e9"
"bytes": "026cb94f302809d19aacc81da19f2156db9c610498310b930d7787d8a2366dadd3"
},
{
"class": "CurvePoint",
"bytes": "024526ff9ab3c9c4cf619166ff897b8c023a6ff01f54c42a921ec1ab564d5a65eb"
"bytes": "03711b30de53e38ba240d34e796e09b8eabe11c385a02a6f87eb8512e1c3fff690"
}
],
"output": "072c408c4631491eb12b00b38b8f7f20080b802e3e7c5c421f89887055248b1a"
"output": "fb39d851bd5f661406a4f2101b18023aadba39b966f605402179d95c07696f16"
}
]
}

View File

@ -1,24 +1,24 @@
{
"name": "Test vectors for CurveScalar operations",
"params": "default",
"first operand": "d63d8806eba7bfc2f5fd77d21aa5d7cc7cffcee26ac096f7c5904629c0db1c12",
"second operand": "c2cc9d2f0b39201a5d4d4aa755c0506eab19c1abc89068d216f23f4965427ac4",
"first operand": "0a686fbc44fdc4713b7901c88fe416cc9b187501e671a4c70781742f3bcef1ec",
"second operand": "6af63df254cc9dd5728e098ffe9ad3cc1e0252b2e3562522907cab1a41656895",
"vectors": [
{
"operation": "Addition",
"result": "990a2535f6e0dfdd534ac2797066283c6d6ab3a784085f8e1cb026e655e75595"
"result": "755eadae99ca6246ae070b588e7eea98b91ac7b4c9c7c9e997fe1f497d345a81"
},
{
"operation": "Subtraction",
"result": "1370ead7e06e9fa898b02d2ac4e5875dd1e60d36a2302e25ae9e06e05b98a14e"
"result": "9f7231c9f031269bc8eaf838914942ff37c4ff35b2641fe036d727a1ca9fca98"
},
{
"operation": "Multiplication",
"result": "88cdbd2959262c74f26d4315e65b7e8c4fb5d1326fb9f1c6dbfd7c951d43485f"
"result": "a0bc896003f0e4feca2176f978b2cfa99ca73af19bf38782064bc137d9f00169"
},
{
"operation": "Inverse",
"result": "663c74e198bd4dcd2db7b78895fe8994a727d8bcba073818475a22483bb0103a"
"result": "e84f604508a66bfa6df07529238588040aee19b38c68330c031715f478426873"
}
]
}