-
Notifications
You must be signed in to change notification settings - Fork 71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Simplified API #159
Simplified API #159
Conversation
Codecov Report
@@ Coverage Diff @@
## master #159 +/- ##
==========================================
- Coverage 96.1% 95.53% -0.58%
==========================================
Files 12 12
Lines 1129 1141 +12
==========================================
+ Hits 1085 1090 +5
- Misses 44 51 +7
Continue to review full report at Codecov.
|
umbral/pre.py
Outdated
|
||
components = splitter(capsule_bytes) | ||
return cls(*components) | ||
return cls(*components, params=params) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh right, that's obviously needed. It's a little unnerving that, without this, we didn't have a failing test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, that's why I'm leaning towards removing redundant params
as argument in some places (assuming there's a capsule or other structure already holding params
) and making it non-optional in others (e.g., in Umbral key generation).
@@ -132,6 +132,8 @@ def _set_cfrag_correctness_key(self, key_type, key: UmbralPublicKey): | |||
if current_key is None: | |||
if key is None: | |||
raise TypeError("The {} key is not set and you didn't pass one.".format(key_type)) | |||
elif self._umbral_params != key.params: | |||
raise TypeError("You are trying to set a key with different UmbralParameters.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Come on man, this is what segfaults are for.
c0d5c3b
to
190b0f0
Compare
0d28fa6
to
09832ec
Compare
@@ -28,43 +35,52 @@ def test_capsule_creation(alices_keys): | |||
|
|||
|
|||
def test_capsule_equality(): | |||
one_capsule = Capsule(point_e=Point.gen_rand(), | |||
params = default_params() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We pass this in a ton. Should we make this a pytest fixture?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we should. Perhaps in another PR?
@@ -39,26 +40,29 @@ def test_activated_capsule_serialization(alices_keys, bobs_keys): | |||
delegating_pubkey = delegating_privkey.get_pubkey() | |||
signer_alice = Signer(signing_privkey) | |||
|
|||
receiving_privkey, receiving_pubkey = bobs_keys | |||
params = delegating_privkey.params |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an interesting paradigm where each key and capsule can have its own params.
This might be a great pattern in the future, but we should be careful with it for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed on both points 100%. Interesting but be careful.
tests/test_simple_api.py
Outdated
|
||
|
||
@pytest.mark.parametrize("N, M", parameters) | ||
def test_lifecycle_with_serialization(N, M, curve=default_curve()): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Looks really good.
umbral/fragments.py
Outdated
@@ -148,7 +150,7 @@ def to_bytes(self) -> bytes: | |||
|
|||
return result | |||
|
|||
def _bn_keytes__(self): | |||
def _bytes__(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be __bytes__
:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
umbral/keys.py
Outdated
@@ -117,6 +114,12 @@ def to_bytes(self, password: bytes=None, _scrypt_cost: int=20, | |||
|
|||
return umbral_privkey | |||
|
|||
def __bytes__(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't offer __bytes__
on UmbralPrivateKey
to prevent accidental misuse.
I think this should be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @tuxxy here. What's the use case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
None. It was a silly move 🤦♂️ ... Fixed.
@@ -82,8 +79,8 @@ def from_bytes(cls, key_bytes: bytes, params: UmbralParameters=None, | |||
bn_key = CurveBN.from_bytes(key_bytes, params.curve) | |||
return cls(bn_key, params) | |||
|
|||
def to_bytes(self, password: bytes=None, _scrypt_cost: int=20, | |||
encoder: Callable=None): | |||
def to_bytes(self, password: bytes = None, _scrypt_cost: int = 20, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In instances where you set a default value with type in the function like the _scrypt_cost
variable here, the equal sign should have no spaces such that:
_scrypt_cost: int=20
In cases where there is no type, such as None
, it should have a space:
password: bytes = None
https://www.python.org/dev/peps/pep-0008/#other-recommendations
I'm not necessarily a huge fan of this PEP8 rule, as such I will leave it up to everyone else to decide how to do this. Generally, I'm in favor of not doing this as it just isn't necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used to think this was a really dumb style, but then I came around to it. The thing is: in kwargs, x=y
looks like assignment. With the space around it, your eyes learn to treat the space as a type hint instead of argument assignment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 But it's not a type hint? It's a default assignment if the argument is not passed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eh? It is a type hint. Without the space, it looks like you're assigning the value None
to the name bytes
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see! Gotcha.
umbral/signing.py
Outdated
@@ -21,7 +20,7 @@ class Signature: | |||
between (r, s) and DER formatting. | |||
""" | |||
|
|||
def __init__(self, r: int, s: int): | |||
def __init__(self, r: CurveBN, s: CurveBN): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're going to use CurveBN
here, this should be in a separate PR. I'm not quite sure what the best options here are yet and I want to think about it more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, agreed.
umbral/signing.py
Outdated
|
||
def __bytes__(self): | ||
return self.r.to_bytes(32, "big") + self.s.to_bytes(32, "big") | ||
return self.r.to_bytes() + self.s.to_bytes() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One of the best PRs in the history of this project. Bravo.
There are some outstanding concerns, though, so I'll wait a bit to approve.
@@ -28,43 +35,52 @@ def test_capsule_creation(alices_keys): | |||
|
|||
|
|||
def test_capsule_equality(): | |||
one_capsule = Capsule(point_e=Point.gen_rand(), | |||
params = default_params() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
one_capsule = Capsule(point_e=Point.gen_rand(), | ||
params = default_params() | ||
|
||
one_capsule = Capsule(params, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow - big change.
This is pretty cool I think.
But I'm left wondering: if Capsule
is going to require params, does it perhaps make sense to make it private? Is there really ever a public reason for someone to init a Capsule
?
delegating_privkey, _signing_privkey = alices_keys | ||
|
||
sym_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey().point_key) | ||
sym_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh hallelujah. Holy shit what a relief.
assert len(sym_key) == 32 | ||
|
||
# The symmetric key sym_key is perhaps used for block cipher here in a real-world scenario. | ||
sym_key_2 = pre._decapsulate_original(delegating_privkey.bn_key, capsule) | ||
sym_key_2 = pre._decapsulate_original(delegating_privkey, capsule) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh my goodness this is seriously amazing. This is how pyUmbral has always wanted to read. (Although again, as I outlined in #161, I might advocate even going all the way down to just delegating_key
or delegating_secret
and dropping the "priv" idea).
@@ -39,26 +40,29 @@ def test_activated_capsule_serialization(alices_keys, bobs_keys): | |||
delegating_pubkey = delegating_privkey.get_pubkey() | |||
signer_alice = Signer(signing_privkey) | |||
|
|||
receiving_privkey, receiving_pubkey = bobs_keys | |||
params = delegating_privkey.params |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed on both points 100%. Interesting but be careful.
* Instead, takes params from Capsules or UmbralPublicKeys * Makes params required in some places (Capsule.init, Capsule.from_bytes, etc) * Removes pre.CHACHA20_KEY_SIZE constant and use dem.DEM_KEYSIZE instead
* Functions in `pre` now only take Umbral keys as arguments, rather than primitive types (Point, CurveBN) * Remove unnecessary arguments from public facing and internal methods when they can be extracted from a Capsule, UmbralPublicKey or UmbralPrivateKey * Adds a getter in Capsule for correctness keys * Adapts the test suite to new simplified API
09832ec
to
9e126bd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🐧
What this does:
This PR provides a slightly simplified API for pyUmbral. In particular, removes all redundant arguments from public and private methods that could be obtained by other means (primarily, from a neighboring argument), such as
params
and correctness keys. The result is not only that the interface is simplified, but also that there is less surface for misuse.umbral.pre
andumbral.fragments
:Capsule
,UmbralPublicKey
orUmbralPrivateKey
.Capsule.attach_cfrag()
that allowed to attach incorrectCFrag
Capsule
for correctness keyspre.CHACHA20_KEY_SIZE
constant and usesdem.DEM_KEYSIZE
instead__eq__
method toUmbralParameters