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

Use extended Euclidean algorithm for modular inverse #7

Closed
wants to merge 6 commits into from

Conversation

swenson
Copy link

@swenson swenson commented Oct 5, 2013

This roughly doubles the speed of the ed25519 implementation in Python.

For whatever reason, this is faster than the previous squaring method.

Before:

Time generate
2.8231959343

Time create signature
5.24891996384

Time verify signature
7.75262784958

After:

Time generate
1.58998298645

Time create signature
3.2284090519

Time verify signature
4.52029514313

div = b
while True:
if div == 0:
return u
Copy link
Member

Choose a reason for hiding this comment

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

This should be 4 space indented.

Copy link
Member

Choose a reason for hiding this comment

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

Also, might be easier to write this as while div != 0 and put return u outside the loop. I don't have a strong opinion on that, but it's how I've seen most reference implementations write it.

Copy link
Author

Choose a reason for hiding this comment

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

Sure.

@dstufft
Copy link
Member

dstufft commented Oct 5, 2013

Important to ask, Are you OK with this code being licensed as in #6 that PR is just waiting on the OK from the other contributor before i merge it.

while True:
if div == 0:
return u
qq, div, d = d // div, d % div, div
Copy link
Member

Choose a reason for hiding this comment

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

d // div, d %div might be better implemented with divmod(d, div), might save a few cycles.

Copy link
Author

Choose a reason for hiding this comment

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

Ah, I forgot about divmod. I'll double check, but you're probably right.

Copy link
Member

Choose a reason for hiding this comment

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

At least on pypy, divmod actually saves instructions (both div and mod are
implemented in terms of divmod), I assume this is true on CPython as well

On Fri, Oct 4, 2013 at 9:56 PM, Christopher Swenson <
[email protected]> wrote:

In ed25519.py:

  •   are both quadratic, so this is likely the fastest way to do this.
    
  •   See, for example, H. Cohen, A Course in Computational Algebraic Number
    
  •   Theory, Algorithm 1.3.6
    
  • """
  • a, b = z, q
  • if b == 0:
  •    return 1
    
  • u = 1
  • d = a
  • r = 0
  • div = b
  • while True:
  •    if div == 0:
    
  •      return u
    
  •    qq, div, d = d // div, d % div, div
    

Ah, I forgot about divmod. I'll double check, but you're probably right.


Reply to this email directly or view it on GitHubhttps://github.com//pull/7/files#r6782936
.

"I disapprove of what you say, but I will defend to the death your right to
say it." -- Evelyn Beatrice Hall (summarizing Voltaire)
"The people's good is the highest law." -- Cicero
GPG Key fingerprint: 125F 5C67 DFE9 4084

@swenson
Copy link
Author

swenson commented Oct 5, 2013

@dstufft I left a comment on the license PR -- I'm actually not okay with that license, but mostly because it is contradictory.

* Fix docstring formatting
* Use divmod for a speedup
* Cleanup 2-spacing
* Remove unused variables
@swenson
Copy link
Author

swenson commented Oct 5, 2013

Updated, now even faster:

Time generate
1.10641002655
Time create signature
2.36265206337
Time verify signature
3.46614480019

"""
d, div = z, q
if div == 0:
return 1
Copy link
Member

Choose a reason for hiding this comment

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

q will never be 0, so we can just drop this case, as far as I can tell.

Copy link
Author

Choose a reason for hiding this comment

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

Right.

@alex
Copy link
Member

alex commented Oct 5, 2013

Modulo that comment, and the licensing issue, I think this looks good. I want to get another set of eyes on it though.

@alex
Copy link
Member

alex commented Oct 5, 2013

This change seems to have caused it to recurse infinetly, and I don't understand why.

@@ -92,7 +90,7 @@ def scalarmult(P, e):
if e == 0:
return (0, 1)

Q = scalarmult(P, e // 2)
Q = scalarmult(P, e / 2)
Copy link
Member

Choose a reason for hiding this comment

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

Why this change?

Copy link
Author

Choose a reason for hiding this comment

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

Not sure how that wound up in there. I'll change it back.

@@ -39,22 +39,20 @@ def pow2(x, p):
p -= 1
return x
Copy link
Member

Choose a reason for hiding this comment

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

pow2 can be deleted now

@gnprice
Copy link
Contributor

gnprice commented Oct 5, 2013

Great that this is faster! Glad to see more eyes on the crypto algorithms here.

The algorithm looks right, and standard. The one drawback, which is important in some contexts, is that it introduces branches, which means it's vulnerable to timing attacks. This is the reason why DJB didn't use this algorithm for Curve25519, a precursor of this system, and presumably also why he and his collaborators didn't for Ed25519.

The Curve25519 paper explains:

I compute X0 (nQ) = xz p−2 using a straightforward sequence of 254 squarings and 11 multiplications. This is about 7% of the Curve25519 computation. An extended-Euclid inversion of z, randomized to protect against timing attacks, might be faster, but the maximum potential speedup is very small, while the cost in code complexity is large.

http://cr.yp.to/ecdh/curve25519-20060209.pdf

(This version isn't randomized to protect against timing attacks, and I don't think we'd want to implement such a version.) The Ed25519 paper just says it does inversion the same way as in Curve25519.

This is no problem as long as this code is only used for verifying signatures on public data. It's a serious problem for making signatures. We should use this algorithm only if we remove the signing code from this pure-Python fallback, to make sure nobody unwittingly falls into that vulnerability. Or if we use this algorithm for verifying signatures, and another one for making them. Either way, there should also be a comment warning of the danger.

Our existing and previous implementations haven't been carefully secured against branches or loads that depend on potentially secret data -- there might well be such things hidden in the implementation of e.g. long multiplication -- but I believe in principle they could be. At the Python level they're free of such things.

@swenson
Copy link
Author

swenson commented Oct 5, 2013

@gnprice Great point. We can definitely look into reviving the other inv(z) implementation if we want to guard against that.

@alex
Copy link
Member

alex commented Oct 5, 2013

Nice catch @gnprice. I seriously doubt any of this code is timing attack safe (I'm pretty suspicious that you can reliably writing timing attack safe code which runs on CPython or PyPy in general). At a minimum starting to clearly document this would be valuable.

@dstufft
Copy link
Member

dstufft commented Oct 5, 2013

So for the use case of why I wanted to make ed25519 fast verifying signatures is all we needed. However I do think it'd be nice to have the other operations just for completeness sake. I don't understand the PR itself but if it makes sense to have two implementations for different use cases I think that's reasonable?

@swenson
Copy link
Author

swenson commented Oct 5, 2013

In that case, I think it would make more sense to have a separate file, say, ed25519_safe.py, that had timing-attack resistant implementations of everything.

Or, alternatively, make ed25519.py be the safe one, but also have ed25519_fast.py.

And maybe make a note that if you want it safe and fast, that you should use a C library. :)

@alex
Copy link
Member

alex commented Oct 5, 2013

I think before we delve into having multiple APIs, we really need to establish if a) this is remotely timing attack safe now, or b) it has any hope of being so in pure-python.

@swenson
Copy link
Author

swenson commented Oct 5, 2013

@alex I'm not sure it is timing attack resistant at all right -- the scalarmult function is very sensitive, for instance.

@alex
Copy link
Member

alex commented Oct 5, 2013

I'm assuming the word "now" is missing there?

On Sat, Oct 5, 2013 at 9:37 AM, Christopher Swenson <
[email protected]> wrote:

@alex https://github.com/alex I'm not sure it is timing attack
resistant at all right -- the scalarmult function is very sensitive, for
instance.


Reply to this email directly or view it on GitHubhttps://github.com//pull/7#issuecomment-25751871
.

"I disapprove of what you say, but I will defend to the death your right to
say it." -- Evelyn Beatrice Hall (summarizing Voltaire)
"The people's good is the highest law." -- Cicero
GPG Key fingerprint: 125F 5C67 DFE9 4084

@swenson
Copy link
Author

swenson commented Oct 5, 2013

Yup. Right now.
On Oct 5, 2013 9:41 AM, "Alex Gaynor" [email protected] wrote:

I'm assuming the word "now" is missing there?

On Sat, Oct 5, 2013 at 9:37 AM, Christopher Swenson <
[email protected]> wrote:

@alex https://github.com/alex I'm not sure it is timing attack
resistant at all right -- the scalarmult function is very sensitive, for
instance.


Reply to this email directly or view it on GitHub<
https://github.com/pyca/ed25519/pull/7#issuecomment-25751871>
.

"I disapprove of what you say, but I will defend to the death your right
to
say it." -- Evelyn Beatrice Hall (summarizing Voltaire)
"The people's good is the highest law." -- Cicero
GPG Key fingerprint: 125F 5C67 DFE9 4084


Reply to this email directly or view it on GitHubhttps://github.com//pull/7#issuecomment-25751927
.

@alex
Copy link
Member

alex commented Oct 5, 2013

I started documenting this in #11

@swenson
Copy link
Author

swenson commented Oct 5, 2013

Superceded by homogeneous coordinate version.

@swenson swenson closed this Oct 5, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants