Re-encryption with correctness proofs#115
Conversation
…rify_correctness_proof
TODO: Figure out final size for CorrectnessProofs
…the incorrect cfrags
…ctness-proof # Conflicts: # umbral/pre.py
tuxxy
left a comment
There was a problem hiding this comment.
Looks good, see my comments.
| 'timestamp' : time.time(), | ||
| 'capsule' : bytes(capsule), | ||
| } | ||
| metadata = str(metadata).encode() |
There was a problem hiding this comment.
Obviously, we want a better form of serialization than just turning a dict into a string.
I presume this is being blocked due to not having a format for metadata in the KMS?
There was a problem hiding this comment.
Well, to me this is just a rough example for testing purposes. We want something reasonable, beyond a simple b"this is the metadata of a request", but without being something too complex.
There was a problem hiding this comment.
In a way, I think b"this is the metadata of a request" is better.
In this case, we're showing casting a dict, with an int for ursula_id, and time.time()? I mean, are we really nudging the user in a good direction here?
I think it's probably better to either structure metadata or leave it as flat, arbitrary bytes.
There was a problem hiding this comment.
OK, let's go the the initial approach then.
umbral/fragments.py
Outdated
| sig = BigNum.from_bytes(data.read(key_size), curve) | ||
|
|
||
| metadata = data.read() | ||
| if metadata == bytes(0): |
There was a problem hiding this comment.
This is build a bit weirdly.
You can simply do metadata = data.read() or None. If there is no data, it will be b'' which loses precedence to None, then you don't have to set it to None.
That is unless you are setting metadata to be 0x00 and it will always have that byte, but I don't like that.
umbral/fragments.py
Outdated
| + sig | ||
|
|
||
| if self.metadata is not None: | ||
| result = result + self.metadata |
There was a problem hiding this comment.
You can also do result += self.metadata or b''. This will concat the empty bytestring if self.metadata is None and removes the need for L135.
umbral/fragments.py
Outdated
|
|
||
| return cls(e1, v1, kfrag_id, eph_ni) | ||
| proof = data.read() | ||
| proof = CorrectnessProof.from_bytes(proof, curve) if proof != bytes(0) else None |
There was a problem hiding this comment.
Same logic applies here from what I listed above.
| serialized_cfrag = e1 + v1 + kfrag_id + eph_ni | ||
|
|
||
| if self.proof is not None: | ||
| serialized_cfrag += self.proof.to_bytes() |
There was a problem hiding this comment.
mmm, no, it fails when self.proof is None. I'll revert it to the previous way.
|
|
||
| def _open_capsule(capsule: Capsule, bob_private_key: UmbralPrivateKey, | ||
| alice_pub_key: UmbralPublicKey, params: UmbralParameters=None) -> bytes: | ||
| def _open_capsule(capsule: Capsule, bob_privkey: UmbralPrivateKey, |
| priv_b = bob_private_key.bn_key | ||
| pub_b = priv_b * params.g | ||
| priv_b = bob_privkey.bn_key | ||
| pub_b = bob_privkey.get_pubkey().point_key |
There was a problem hiding this comment.
If my PR gets in before this one, you can do bob_privkey.pubkey.point_key because it gets cached.
| pub_a = alice_pub_key.point_key | ||
| pub_a = alice_pubkey.point_key | ||
|
|
||
| # TODO: Change dict for a list if issue #116 goes through |
| def _challenge(kfrag: KFrag, capsule: Capsule, | ||
| cfrag: CapsuleFrag, challenge_metadata: bytes=None, | ||
| params: UmbralParameters=None) -> ChallengeResponse: | ||
| def _prove_correctness(cfrag: CapsuleFrag, kfrag: KFrag, capsule: Capsule, |
There was a problem hiding this comment.
I think _prove_correctness has to be above reencrypt for it to be used?
There was a problem hiding this comment.
Might it also be better as a classmethod on CorrectnessProof?
| class UmbralCorrectnessError(GenericUmbralError): | ||
| def __init__(self, message, offending_cfrags): | ||
| super().__init__(message) | ||
| self.offending_cfrags = offending_cfrags |
There was a problem hiding this comment.
Is there a need to capture these here even if we handle them ~L485?
There was a problem hiding this comment.
The idea is to include them in the exception, so in the application (i.e., the KMS), Bob could know which cfrags were incorrect and act consequently.
| cfrag = pre.reencrypt(kfrag, capsule, metadata=metadata) | ||
| capsule.attach_cfrag(cfrag) | ||
|
|
||
| assert pre._verify_correctness(capsule, cfrag, |
There was a problem hiding this comment.
I like the simplicity of the interface!
jMyles
left a comment
There was a problem hiding this comment.
This is a clear step forward in terms of the tooling. I think that exactly how Bob can expect to benefit needs to be reflected with updated documentation.
|
|
||
| from umbral import pre, keys | ||
| from umbral.point import Point | ||
| from umbral.fragments import CorrectnessProof |
There was a problem hiding this comment.
Looks like you imported this and then didn't use it?
There was a problem hiding this comment.
I was using it through pre.CorrectnessProof, which was it's previous location. I forgot to change it. Now it's fixed.
| 'timestamp' : time.time(), | ||
| 'capsule' : bytes(capsule), | ||
| } | ||
| metadata = str(metadata).encode() |
There was a problem hiding this comment.
In a way, I think b"this is the metadata of a request" is better.
In this case, we're showing casting a dict, with an int for ursula_id, and time.time()? I mean, are we really nudging the user in a good direction here?
I think it's probably better to either structure metadata or leave it as flat, arbitrary bytes.
|
|
||
| # Alternatively, we can try to open the capsule directly. | ||
| # We should get an exception with an attached list of incorrect cfrags | ||
| with pytest.raises(pre.UmbralCorrectnessError) as exception_info: |
umbral/pre.py
Outdated
| try: | ||
| proof = cfrag.proof | ||
| except AttributeError: | ||
| raise ValueError("CFrag doesn't have a correctnes proof attached") |
There was a problem hiding this comment.
Untested - needs a test I think.
There was a problem hiding this comment.
Actually, it's better to remove the try, since it doesn't fail when cfrag.proof is set to None, which will fail anyway a few lines below. So, I wrote a test that checks that the AttributeError is raised when there's no proof but one is expected. It's called test_decryption_fails_when_it_expects_a_proof_and_there_isnt.
|
|
||
| if self.metadata is not None: | ||
| result = result + self.metadata | ||
| result += self.metadata or b'' |
There was a problem hiding this comment.
This is an odd line - the user is not allowed to use something falsey as metadata?
What this does:
ChallengeResponsetoCorrectnessProof, and integrates it insideCFrags(Solves Integrate the challenge protocol as part of the re-encryption request/response #100)provide_proof.UmbralCorrectnessErroris raised, which includes a list of theCFragsthat don't pass the correctness verification (so that Bob can take the action he considers more convenient).CorrectnessProof(and hence, into theCFrag). As a consequence, metadata is now a parameter ofpre.reencrypt()(although optional).bytesobject (Closes Define format for re-encryption metadata? #120)