Skip to content
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

[NO-MERGE] Unified Capsule instead of ReconstructedCapsule #24

Closed
wants to merge 14 commits into from

Conversation

jMyles
Copy link
Contributor

@jMyles jMyles commented Jan 18, 2018

What this does:

  1. Removes ReconstructedCapsule
  2. Provides facilities for instantiating a Capsule from reconstructed components
  3. Establishes public interfaces for Bob to open the capsule and get the contents inside.

Copy link
Contributor

@tuxxy tuxxy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other than the readability and uniformity issues, it looks okay.

Keep in mind this is a reference implementation and it needs to be readable in comparison to the paper.

umbral/umbral.py Outdated

return key
d = hash_to_bn([xcomp, pub_key, xcomp * priv_key], self.params)
shared_key = capsule.reconstructed_ephemeral_points() * d
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't do this, this is not clear at all with what's happening. It doesn't look like DH anymore.

h = hash_to_bn([e, v], self.params)
inv_d = ~d
assert orig_pub_key * (s * inv_d) == capsule._point_eph_v_prime + (
capsule._point_eph_e_prime * h), "Generic Umbral Error"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really hard to read, can you leave in the e_prime and v_prime vars?

umbral/umbral.py Outdated
def decapsulate_original(self, priv_key, capsule, key_length=32):
"""Derive the same symmetric key"""

shared_key = (capsule.point_eph_e + capsule.point_eph_v) * priv_key
shared_key = capsule.original_ephemeral_points() * priv_key
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't look like it's doing DH anymore. :/

umbral/umbral.py Outdated

return ReconstructedCapsule(e_prime, v_prime, eph_ni)

def to_bytes(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you getting rid of to_bytes? Keep this for uniformity, it's how cryptographic APIs handle their bytestring representations.

umbral/umbral.py Outdated

@staticmethod
def from_bytes(data: bytes, curve: ec.EllipticCurve):
def _reconstructed_bytes(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep it as from_bytes, we're going to also want this "public".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what you mean here. This isn't a constructor method like from_bytes. Maybe you misread?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, you're right. Probably will also want to do how you're doing from_bytes above with the boolean flag, except do it as to_bytes here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.

umbral/umbral.py Outdated
return self._point_eph_e + self._point_eph_v

def reconstructed_ephemeral_points(self):
return self._point_eph_e_prime + self._point_eph_v_prime
Copy link
Contributor

@tuxxy tuxxy Jan 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rather call these vars individually for more readability. This also does point addition, did you notice that? Whenever that happens, it should happen explicitly and not hidden in a method call.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...why?

I don't think that three fairly obscure var names are more (or even as) readable as a single method name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because you're hiding point addition -- this does a few things that we don't want.
- Since this is a reference implementation, anywhere in the code should be really explicit when we're performing a curve operation.
- This is a dangerous operation and shouldn't be exposed as a simple method that the user can confuse for something else. This method is not clear that it performs addition, even with the operator overloading.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another issue here are the names, I just realized:

original_ephemeral_points -> It's not returning plural points, after the addition it returns one and they're not the original ones either. Same for reconstructed_ephemeral_points.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, that's a good point.

I still think though that returning them from a single uniform method is much safer.

For the smaller userbase that is willing to assume the liability, the variables are available as private attributes. But the better approach is to provide a method that does the addition, the correct way, with the correct two values, and to make that method the public default.

So: how about if these two methods instead return a tuple of the points?

This also has the benefit of forcing the candor in the implementation that you're after, ie:

sum(capsule.original_ephemeral_points) * priv_key

Copy link
Contributor

@tuxxy tuxxy Jan 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that's just not clear to me. Can we just leave it as it was? It makes it so much more clear.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm interested in other perspectives here. @cygnusv, @michwill, @KPrasch?

What's the advantage of forcing the user to try to do it properly instead of giving them the method that does (but also providing the primitives)?

Consider:

shared_key = (capsule.point_eph_e + capsule.point_eph_v) * priv_key

Have I used the correct two points?

  • Did I use the original if that's what I wanted?
  • Did I use the reconstructed if that's what I wanted?
  • Did I use both from the same set? I didn't intermix, did I?
  • Am I sure I didn't let the sig or noninteractive_point slip in there? Definiely e and v, right?

...especially since there's no great way to repr Points from each other in such as way that makes clear their role.

Now, did I add them in the correct order?

Now consider:

shared_key = sum(capsule.original_ephemeral_points) * priv_key

There's no way to mess that up. I know that I have the original points, I know that I have the correct two values, that they're in the right order, that they're added properly. Nothing is left to chance.

What is bad about this? Are you suggesting that other libraries refuse to do this on principle? If so, I want to see the conversation that led to such a design decision - do you have a link?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't about making it easy for the user. It's about making it so that readers can read the paper, look at our code, and understand what's happening.

We don't need methods that hide operational logic in them. If someone is using our library to perform arithmetic on curves, they're using it wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, we decided not to do this. From Slack:

jmyles 5:51 PM
Getting back original_ephemeral_points for a minute:
5:51
Are you saying that this is never a public notion at all?
tux 5:51 PM
I don’t expect anyone to modify the underlying cryptography of this library. If they do, they probably shouldn’t.
jmyles 5:51 PM
Well, that's not what I asked. 🙂
dnunez 5:52 PM
they are in the test because we need to check the serialization
tux 5:52 PM
Right
dnunez 5:52 PM
but a user would never need to inspect those elements
jmyles 5:52 PM
I see
5:52
ok
dnunez 5:52 PM
for a user a capsule is atomic
jmyles 5:52 PM
So they are used by their respective decapsulate functions and never publicly?
5:52
Ahh
5:52
OK
dnunez 5:52 PM
yes
jmyles 5:52 PM
OK yeah, I withdraw then - I misunderstood.
dnunez 5:53 PM
we only use it in umbral, and in the tests of course
5:53
but there shouldn't be used outside that scope
jmyles 5:53 PM
I see.
5:53
Yeah, that completely makes sense now, reading again
dnunez 5:53 PM
in any case, I don't see why is this related with that method that returns the additon of e and v
jmyles 5:54 PM
Well, my drive was to help the user avoid doing that improperly.
5:54
(ie, with the original 'e' and 'v' when they wanted the reconstructed, etc)
5:55
See, this is part of the problem with writing the first time with too few _private things 🙂
dnunez 5:55 PM
ok, that possibly was a problem, yes
tux 5:56 PM
I thought we all knew that all those fragment pieces were parts of the ciphertext?
5:56
intrinsically private sorta 😅
dnunez 5:57 PM
it was clear to me, at least, but of course I designed it
5:57
and I have to admit that I'm new to python
tux 5:57 PM
Meh, don’t fret about Python privacy, it’s not real privacy.
dnunez 5:57 PM
the _private notation seems weird coming from Java
jmyles 5:57 PM
Yeah, I mean reading again, with the benefit of you're commentary, it makes sense
tux 5:58 PM
It just doesn’t import certain variables prefixed with _
dnunez 5:58 PM
since it's not real
jmyles 5:58 PM
But do you see what I was seeing?
dnunez 5:58 PM
not really
5:58
but I understand that there could have been misunderstandings
jmyles 5:58 PM
I saw shared_key = (capsule.point_eph_e + capsule.point_eph_v) * priv_key in a public method and thought, "well, if the user wanted to..."
michwill 5:58 PM
Well, you can have two underscores for real 🙂
tux 5:59 PM
throws papers
jmyles 5:59 PM
hahahaha
5:59
But seriously: the decapsulate methods are good candidates for __
5:59
for this reason
dnunez 5:59 PM
I don't see what's the potential problem with the user anyway
tux 6:00 PM
^ Yeah, this is really my main question
6:00
I don’t expect users to go muddling around in the code here.
dnunez 6:00 PM
how could the user screw things there?
tux 6:00 PM
It’s not like they’re gonna go, “Oh, well I need to add to the ciphertext!”
jmyles 6:00 PM
Well, what if they wrote a Bob who decapsulates the capsule in his super special way, but used the original e and the reconstrcuted v?
tux 6:00 PM
they should not do that
dnunez 6:00 PM
basically, it would fail
jmyles 6:01 PM
@Tux: if you feel so strongly, then let's have code saying so 🙂
6:01
@Tux: I agree they should not do that - that's why I wrote a method that does it the right way for them
6:01
(not yet realizing that they don't need to do it anyway)
dnunez 6:01 PM
but jmyles
6:01
your argument applies to every line of the code
jmyles 6:01 PM
No, only the public interfaces
dnunez 6:02 PM
I dont understand that
jmyles 6:02 PM
If Capsule.open calls __decapsulate_reconstructed, then it's absolutely obvious that the user isn't allowed to much with the latter without taking complete responsibility for their idiocy
6:02
🙂
dnunez 6:02 PM
ok...
jmyles 6:02 PM
I was trying to remove ReconstructedCapsule in a way that still forced the user to only be able to do the right thing.
6:03
And, at the time, I thought that I was seeing the user calling decapsulate directly, not using the public interfaces on Capsule (because yes, I spaced for a second and wasn't thinking of a capsule as an atomic structure)

return cls(eph_e, eph_v, sig)

@classmethod
def from_reconstructed_bytes(cls, data: bytes, curve: ec.EllipticCurve):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename to from_bytes for uniformity.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, which one is from_bytes? This or from_original_bytes?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove both and leave in from_bytes, and offer a flag for is_reconstructed=False. When True, reconstruct the capsule.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh yes that sounds good.

umbral/umbral.py Outdated
# TODO: Raise an error here if Bob has gathered enough cFrags.
if self.contents and not force_reopen:
newly_opened = True
else:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This newly_opened and force_reopen doesn't really do much for me. It just adds some weird logic in an otherwise plain reference implementation. I don't understand what problem it's solving.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I mostly agree. I just didn't want to leave the user without this functionality without at least sketching and discussing it first.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should probably stay out of this PR until David and I discuss how we're going to handle the symmetric operations when something like this might be possible.

else:
self._reconstruct(pre=pre)
if not pre:
pre = PRE(UmbralParameters())
Copy link
Member

@cygnusv cygnusv Jan 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UmbralParameters() should not be called here. There is only ONE set of parameters for the whole system, and UmbralParameters creates these parameters, where some of them are randomized. Therefore if we create new parameters in several places, things will start not to work. Perhaps I should have used some kind of singleton pattern for UmbralParameters, or something similar.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cygnusv absolutely - I was going to make an issue for this next.

Let's turn the PRE class into a module, and instantiate the one and only UmbralParamteres there.

Consider this a workaround until we do that.

@jMyles jMyles changed the title Unified Capsule instead of ReconstructedCapsule [WIP] Unified Capsule instead of ReconstructedCapsule Jan 24, 2018
@jMyles jMyles changed the title [WIP] Unified Capsule instead of ReconstructedCapsule [NO-MERGE] Unified Capsule instead of ReconstructedCapsule Jan 27, 2018
jMyles added a commit to jMyles/pyUmbral that referenced this pull request Jan 27, 2018
@tuxxy
Copy link
Contributor

tuxxy commented Jan 28, 2018

I'm locking the conversation here. Further discussion should happen in the more recent PR #38.

This will be closed when #38 gets merged.

@nucypher nucypher locked as resolved and limited conversation to collaborators Jan 28, 2018
@tuxxy tuxxy closed this Jan 31, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants