brython-dev / brython

Brython (Browser Python) is an implementation of Python 3 running in the browser
BSD 3-Clause "New" or "Revised" License
6.4k stars 512 forks source link

Inconsistent result of a pow() with a negative exponent #2407

Closed CookiePLMonster closed 8 months ago

CookiePLMonster commented 8 months ago

Calculating a private RSA key via pow(x, -1, phi) appears to fail in Brython.

Minimal reproducible example:

p = 800275543
q = 33679599593
e = 65537
n = p*q
d = pow(e, -1, (p-1) * (q-1))
print(f'd: {d}, n: {n}')

CPython 3.11.3: d: 21203499539617337777, n: 26952959852310653999

Brython 3.11.3 (Brython 3.12.3 yields the same results, however): d: -5749460278213441087, n: 26952959852310653999

CPython's calculation results match WolframAlpha: https://www.wolframalpha.com/input?i=PowerMod%2865537%2C+-1%2C+%28800275543-1%29*%2833679599593-1%29%29

CookiePLMonster commented 8 months ago

To make testing easier, egcd could be used to verify validity of results:

def egcd(a, b):
    if a == 0:
        return (b, 0, 1)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)

def modinv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x % m

p = 800275543
q = 33679599593
e = 65537
phi = (p-1) * (q-1)

assert pow(e, -1, phi) == modinv(e, phi)
coproc commented 8 months ago

The value -5749460278213441087 for d is not really wrong, it is just another representative of the same residuum class modulo phi = (p-1)*(q-1) = 26952959817830778864: d - phi = 21203499539617337777 - 26952959817830778864 = -5749460278213441087

So in the 3-argument version of pow probably only a line like this is missing at the end: if result < 0: result += mod

PierreQuentel commented 8 months ago

Thanks for the report @CookiePLMonster. @coproc you were right, the solution was to make sure that the modulo is positive.