Skip to content

Instantly share code, notes, and snippets.

@Parr0sky
Forked from bellbind/ecc.py
Last active October 16, 2022 07:08
Show Gist options
  • Select an option

  • Save Parr0sky/4ed4df8266ce1ce970e5ecab6bb6df06 to your computer and use it in GitHub Desktop.

Select an option

Save Parr0sky/4ed4df8266ce1ce970e5ecab6bb6df06 to your computer and use it in GitHub Desktop.

Revisions

  1. @LeidyRomero LeidyRomero revised this gist Sep 17, 2020. 1 changed file with 117 additions and 8 deletions.
    125 changes: 117 additions & 8 deletions ecc.py
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,7 @@
    # Basics of Elliptic Curve Cryptography implementation on Python
    import collections

    import decimal
    decimal.getcontext().prec = 5

    def inv(n, q):
    """div on PN modulo a/b mod q as a * inv(b, q) mod q
    @@ -20,24 +21,126 @@ def sqrt(n, q):
    >>> assert (sqrt(n, q)[1] ** 2) % q == n
    """
    assert n < q
    for i in range(1, q):
    if i * i % q == n:
    print("pasa assert")
    for i in range(1, q//2):

    if (((i * i) % q) == n) or (((q-i-1) * (q-i-1)) % q) == n:
    return (i, q - i)
    pass
    raise Exception("not found")


    Coord = collections.namedtuple("Coord", ["x", "y"])

    #from https://eli.thegreenplace.net/2009/03/07/computing-modular-square-roots-in-python#:~:text=Consider%20the%20congruence%20of%20the%20form%3A&text=In%20normal%20arithmetic%2C%20this%20is,root%20of%20n%20modulo%20p.

    def sqrt_mod(a, p):
    """ Find a quadratic residue (mod p) of 'a'. p
    must be an odd prime.
    Solve the congruence of the form:
    x^2 = a (mod p)
    And returns x. Note that p - x is also a root.
    0 is returned is no square root exists for
    these a and p.
    The Tonelli-Shanks algorithm is used (except
    for some simple cases in which the solution
    is known from an identity). This algorithm
    runs in polynomial time (unless the
    generalized Riemann hypothesis is false).
    """
    # Simple cases
    #
    if legendre_symbol(a, p) != 1:
    print("retorna 0s 1")
    return 0,0
    elif a == 0:
    print("retorna 0s 2")
    return 0,0
    elif p == 2:
    print("retorna 0s 3")
    return 0,0
    elif p % 4 == 3:
    print("retorna pows")
    return pow(a, (p + 1) / 4, p), -pow(a, (p + 1) / 4, p)

    # Partition p-1 to s * 2^e for an odd s (i.e.
    # reduce all the powers of 2 from p-1)
    #
    s = p - 1
    e = 0
    while s % 2 == 0:
    s /= 2
    e += 1

    # Find some 'n' with a legendre symbol n|p = -1.
    # Shouldn't take long.
    #
    n = 2
    while legendre_symbol(n, p) != -1:
    n += 1

    # Here be dragons!
    # Read the paper "Square roots from 1; 24, 51,
    # 10 to Dan Shanks" by Ezra Brown for more
    # information
    #

    # x is a guess of the square root that gets better
    # with each iteration.
    # b is the "fudge factor" - by how much we're off
    # with the guess. The invariant x^2 = ab (mod p)
    # is maintained throughout the loop.
    # g is used for successive powers of n to update
    # both a and b
    # r is the exponent - decreases with each update
    #
    x = pow(a, (s + 1) / 2, p)
    b = pow(a, s, p)
    g = pow(n, s, p)
    r = e

    while True:
    t = b
    m = 0
    for m in range(r):
    if t == 1:
    break
    t = pow(t, 2, p)

    if m == 0:
    print("retorna x")
    return x,-x

    gs = pow(g, 2 ** (r - m - 1), p)
    g = (gs * gs) % p
    x = (x * gs) % p
    b = (b * g) % p
    r = m


    def legendre_symbol(a, p):
    """ Compute the Legendre symbol a|p using
    Euler's criterion. p is a prime, a is
    relatively prime to p (if p divides
    a, then a|p = 0)
    Returns 1 if a has a square root modulo
    p, -1 otherwise.
    """
    print(": "+str(a)+","+str(((p - 1) / 2))+","+str(p))
    ls = pow(a, ((p - 1) / 2),p)
    return -1 if ls == p - 1 else ls
    class EC(object):
    """System of Elliptic Curve"""
    def __init__(self, a, b, q):
    """elliptic curve as: (y**2 = x**3 + a * x + b) mod q
    - a, b: params of curve formula
    - q: prime number
    """
    assert 0 < a and a < q and 0 < b and b < q and q > 2
    assert 0 <= a and a < q and 0 < b and b < q and q > 2
    assert (4 * (a ** 3) + 27 * (b ** 2)) % q != 0
    self.a = a
    self.b = b
    @@ -63,8 +166,12 @@ def at(self, x):
    >>> assert ec.is_valid(a) and ec.is_valid(ma)
    """
    assert x < self.q
    print("primero")
    ysq = (x ** 3 + self.a * x + self.b) % self.q
    print("segundo")
    print("vals: "+str(ysq)+","+str(self.q))
    y, my = sqrt(ysq, self.q)

    return Coord(x, y), Coord(x, my)

    def neg(self, p):
    @@ -125,6 +232,7 @@ def order(self, g):
    >>> assert ec.is_valid(a) and ec.mul(a, o) == ec.zero
    >>> assert o <= ec.q
    """
    print(self.is_valid(g))
    assert self.is_valid(g) and g != self.zero
    for i in range(1, self.q + 1):
    if self.mul(g, i) == self.zero:
    @@ -249,17 +357,18 @@ def validate(self, hashval, sig, pub):

    if __name__ == "__main__":
    # shared elliptic curve system of examples
    ec = EC(1, 18, 19)
    ec = EC(0, 7, 115792089237316195423570985008687907853269984665640564039457584007908834671663)
    g, _ = ec.at(7)
    assert ec.order(g) <= ec.q

    mapping = [ec.mul(g, i) for i in range(eg.n)]
    plain = mapping[7]
    print(len(plain.encode('utf-8')))
    # ElGamal enc/dec usage
    eg = ElGamal(ec, g)
    # mapping value to ec point
    # "masking": value k to point ec.mul(g, k)
    # ("imbedding" on proper n:use a point of x as 0 <= n*v <= x < n*(v+1) < q)
    mapping = [ec.mul(g, i) for i in range(eg.n)]
    plain = mapping[7]


    priv = 5
    pub = eg.gen(priv)
  2. @bellbind bellbind revised this gist Dec 5, 2011. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion pn.py
    Original file line number Diff line number Diff line change
    @@ -109,6 +109,6 @@ def jacobi2(a, q):
    a1, e = a1 >> 1, e + 1
    pass
    m8 = q % 8
    s = 1 if m8 % 1 == 0 or m8 == 1 or m8 == 7 else -1
    s = -1 if m8 == 3 or m8 == 5 else 1 # m8 = 0,2,4,6 and 1,7
    if q % 4 == 3 and a1 % 4 == 3: s = -s
    return s if a1 == 1 else s * jacobi2(q % a1, a1)
  3. @bellbind bellbind revised this gist Dec 5, 2011. 1 changed file with 17 additions and 3 deletions.
    20 changes: 17 additions & 3 deletions pn.py
    Original file line number Diff line number Diff line change
    @@ -85,16 +85,30 @@ def sqrt2(n, q):


    def jacobi(a, q):
    """jacobi symbol: judge existing sqrtmod
    """jacobi symbol: judge existing sqrtmod (1: exist, -1: not exist)
    - j(a*b,q) = j(a,q)*j(b,q)
    - j(a*q+b, q) = j(b, q)
    - j(a, 1) = 1
    - j(0, q) = 0
    - j(2, q) = -1 ** (q^2 - 1)/8
    - j(p, q) = -1 ^ {(p - 1)/2} * -1 ^ {(q - 1)/2} * j(q, p)
    - j(p, q) = -1 ^ {(p - 1)/2 * (q - 1)/2} * j(q, p)
    """
    if q == 1: return 1
    if a == 0: return 0
    if a % 2 == 0: return (-1) ** ((q * q - 1) // 8) * jacobi(a // 2, q)
    return (-1) ** ((a - 1) // 2) * (-1) ** ((q - 1) // 2) * jacobi(q % a, a)
    return (-1) ** ((a - 1) // 2 * (q - 1) // 2) * jacobi(q % a, a)

    def jacobi2(a, q):
    """quick jacobi symbol
    - algorithm 2.149 of http://www.cacr.math.uwaterloo.ca/hac/about/chap2.pdf
    """
    if a == 0: return 0
    if a == 1: return 1
    a1, e = a, 0
    while a1 & 1 == 0:
    a1, e = a1 >> 1, e + 1
    pass
    m8 = q % 8
    s = 1 if m8 % 1 == 0 or m8 == 1 or m8 == 7 else -1
    if q % 4 == 3 and a1 % 4 == 3: s = -s
    return s if a1 == 1 else s * jacobi2(q % a1, a1)
  4. @bellbind bellbind revised this gist Dec 5, 2011. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions pn.py
    Original file line number Diff line number Diff line change
    @@ -59,6 +59,7 @@ def sqrt(n, q):

    def sqrt2(n, q):
    """sqrtmod for bigint
    - Algorithm 3.34 of http://www.cacr.math.uwaterloo.ca/hac/about/chap3.pdf
    """
    import random
    # b: some non-quadratic-residue
    @@ -76,8 +77,7 @@ def sqrt2(n, q):
    c = pow(b, t, q)
    r = pow(n, (t + 1) // 2, q)
    for i in range(1, s):
    e = pow(2, s - i - 1, q)
    d = pow(pow(r, 2, q) * ni % q, e, q)
    d = pow(pow(r, 2, q) * ni % q, pow(2, s - i - 1, q), q)
    if d == q - 1: r = r * c % q
    c = pow(c, 2, q)
    pass
  5. @bellbind bellbind revised this gist Dec 5, 2011. 1 changed file with 6 additions and 6 deletions.
    12 changes: 6 additions & 6 deletions pn.py
    Original file line number Diff line number Diff line change
    @@ -61,10 +61,10 @@ def sqrt2(n, q):
    """sqrtmod for bigint
    """
    import random
    # c: some non-quadratic-residue
    c = 0
    while c == 0 or jacobi(c, q) != -1:
    c = random.randint(1, q - 1)
    # b: some non-quadratic-residue
    b = 0
    while b == 0 or jacobi(b, q) != -1:
    b = random.randint(1, q - 1)
    pass
    # q = t * 2^s + 1, t is odd
    t, s = q - 1, 0
    @@ -73,11 +73,11 @@ def sqrt2(n, q):
    pass
    assert q == t * pow(2, s) + 1 and t % 2 == 1
    ni = inv(n, q)
    c = pow(c, t, q)
    c = pow(b, t, q)
    r = pow(n, (t + 1) // 2, q)
    for i in range(1, s):
    e = pow(2, s - i - 1, q)
    d = pow((r * r % q) * ni % q, e, q)
    d = pow(pow(r, 2, q) * ni % q, e, q)
    if d == q - 1: r = r * c % q
    c = pow(c, 2, q)
    pass
  6. @bellbind bellbind revised this gist Dec 5, 2011. 1 changed file with 18 additions and 21 deletions.
    39 changes: 18 additions & 21 deletions pn.py
    Original file line number Diff line number Diff line change
    @@ -61,30 +61,27 @@ def sqrt2(n, q):
    """sqrtmod for bigint
    """
    import random
    m = inv(n, q)
    p, s = q - 1, 0
    while p % 2 == 0:
    p, s = p >> 1, s + 1
    # c: some non-quadratic-residue
    c = 0
    while c == 0 or jacobi(c, q) != -1:
    c = random.randint(1, q - 1)
    pass
    w = 0
    while w == 0 or jacobi(w, q) != -1:
    w = random.randint(1, q - 1)
    # q = t * 2^s + 1, t is odd
    t, s = q - 1, 0
    while t & 1 == 0:
    t, s = t >> 1, s + 1
    pass
    v = pow(w, p, q)
    r = pow(n, (p + 1) // 2, q)
    while True:
    i, z = 0, r * r * m % q
    while i < s and z % q != 1:
    i, z = i + 1, z * z
    pass
    if i == 0:
    return (r, q - r)
    p2 = s - i - 1
    if p2 < 0: raise Exception("not found")
    r = r * pow(v, 1 << p2, q) % q
    assert q == t * pow(2, s) + 1 and t % 2 == 1
    ni = inv(n, q)
    c = pow(c, t, q)
    r = pow(n, (t + 1) // 2, q)
    for i in range(1, s):
    e = pow(2, s - i - 1, q)
    d = pow((r * r % q) * ni % q, e, q)
    if d == q - 1: r = r * c % q
    c = pow(c, 2, q)
    pass
    assert False, "unreached"
    pass
    return (r, q - r)


    def jacobi(a, q):
  7. @bellbind bellbind revised this gist Dec 5, 2011. 1 changed file with 3 additions and 5 deletions.
    8 changes: 3 additions & 5 deletions pn.py
    Original file line number Diff line number Diff line change
    @@ -73,19 +73,17 @@ def sqrt2(n, q):
    v = pow(w, p, q)
    r = pow(n, (p + 1) // 2, q)
    while True:
    i = 0
    z = r * r * m % q
    i, z = 0, r * r * m % q
    while i < s and z % q != 1:
    i = i + 1
    z = z * z
    i, z = i + 1, z * z
    pass
    if i == 0:
    return (r, q - r)
    p2 = s - i - 1
    if p2 < 0: raise Exception("not found")
    r = r * pow(v, 1 << p2, q) % q
    pass
    assert False, "not reached"
    assert False, "unreached"
    pass


  8. @bellbind bellbind revised this gist Dec 5, 2011. 1 changed file with 6 additions and 5 deletions.
    11 changes: 6 additions & 5 deletions pn.py
    Original file line number Diff line number Diff line change
    @@ -39,7 +39,7 @@ def inv2(n, q):
    s, p2, p = 1, n, q - 2
    while p > 0:
    if p & 1 == 1: s = s * p2 % q
    p, p2 = p >> 1, p2 * p2 % q
    p, p2 = p >> 1, pow(p2, 2, q)
    pass
    return s

    @@ -51,7 +51,7 @@ def sqrt(n, q):
    """
    assert n < q
    for i in range(1, q):
    if i * i % q == n:
    if pow(i, 2, q) == n:
    return (i, q - i)
    pass
    raise Exception("not found")
    @@ -70,8 +70,8 @@ def sqrt2(n, q):
    while w == 0 or jacobi(w, q) != -1:
    w = random.randint(1, q - 1)
    pass
    v = w ** p % q
    r = n ** ((p + 1) // 2) % q
    v = pow(w, p, q)
    r = pow(n, (p + 1) // 2, q)
    while True:
    i = 0
    z = r * r * m % q
    @@ -83,7 +83,7 @@ def sqrt2(n, q):
    return (r, q - r)
    p2 = s - i - 1
    if p2 < 0: raise Exception("not found")
    r = r * (v ** (1 << p2) % q) % q
    r = r * pow(v, 1 << p2, q) % q
    pass
    assert False, "not reached"
    pass
    @@ -102,3 +102,4 @@ def jacobi(a, q):
    if a == 0: return 0
    if a % 2 == 0: return (-1) ** ((q * q - 1) // 8) * jacobi(a // 2, q)
    return (-1) ** ((a - 1) // 2) * (-1) ** ((q - 1) // 2) * jacobi(q % a, a)

  9. @bellbind bellbind revised this gist Dec 5, 2011. 2 changed files with 109 additions and 25 deletions.
    30 changes: 5 additions & 25 deletions ecc.py
    Original file line number Diff line number Diff line change
    @@ -6,32 +6,12 @@ def inv(n, q):
    """div on PN modulo a/b mod q as a * inv(b, q) mod q
    >>> assert n * inv(n, q) % q == 1
    """
    # n*inv % q = 1 => n*inv = q*m + 1 => n*inv + q*-m = 1
    # => egcd(n, q) = (inv, -m, 1) => inv = egcd(n, q)[0] (mod q)
    return egcd(n, q)[0] % q
    #[ref] naive implementation
    #for i in range(q):
    # if (n * i) % q == 1:
    # return i
    # pass
    #assert False, "unreached"
    #pass


    def egcd(a, b):
    """extended GCD
    returns: (s, t, gcd) as a*s + b*t == gcd
    >>> s, t, gcd = egcd(a, b)
    >>> assert a % gcd == 0 and b % gcd == 0
    >>> assert a * s + b * t == gcd
    """
    s0, s1, t0, t1 = 1, 0, 0, 1
    while b > 0:
    q, r = divmod(a, b)
    a, b = b, r
    s0, s1, t0, t1 = s1, s0 - q * s1, t1, t0 - q * t1
    for i in range(q):
    if (n * i) % q == 1:
    return i
    pass
    return s0, t0, a
    assert False, "unreached"
    pass


    def sqrt(n, q):
    104 changes: 104 additions & 0 deletions pn.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,104 @@
    # Appendix: improved modulo calc

    def inv(n, q):
    """div on PN modulo a/b mod q as a * inv(b, q) mod q
    >>> assert n * inv(n, q) % q == 1
    """
    # n*inv % q = 1 => n*inv = q*m + 1 => n*inv + q*-m = 1
    # => egcd(n, q) = (inv, -m, 1) => inv = egcd(n, q)[0] (mod q)
    return egcd(n, q)[0] % q
    #[ref] naive implementation
    #for i in range(q):
    # if (n * i) % q == 1:
    # return i
    # pass
    #assert False, "unreached"
    #pass


    def egcd(a, b):
    """extended GCD
    returns: (s, t, gcd) as a*s + b*t == gcd
    >>> s, t, gcd = egcd(a, b)
    >>> assert a % gcd == 0 and b % gcd == 0
    >>> assert a * s + b * t == gcd
    """
    s0, s1, t0, t1 = 1, 0, 0, 1
    while b > 0:
    q, r = divmod(a, b)
    a, b = b, r
    s0, s1, t0, t1 = s1, s0 - q * s1, t1, t0 - q * t1
    pass
    return s0, t0, a

    def inv2(n, q):
    """another PN invmod: from euler totient function
    - n ** (q - 1) % q = 1 => n ** (q - 2) % q = n ** -1 % q
    """
    assert q > 2
    s, p2, p = 1, n, q - 2
    while p > 0:
    if p & 1 == 1: s = s * p2 % q
    p, p2 = p >> 1, p2 * p2 % q
    pass
    return s


    def sqrt(n, q):
    """sqrt on PN modulo: returns two numbers or exception if not exist
    >>> assert (sqrt(n, q)[0] ** 2) % q == n
    >>> assert (sqrt(n, q)[1] ** 2) % q == n
    """
    assert n < q
    for i in range(1, q):
    if i * i % q == n:
    return (i, q - i)
    pass
    raise Exception("not found")


    def sqrt2(n, q):
    """sqrtmod for bigint
    """
    import random
    m = inv(n, q)
    p, s = q - 1, 0
    while p % 2 == 0:
    p, s = p >> 1, s + 1
    pass
    w = 0
    while w == 0 or jacobi(w, q) != -1:
    w = random.randint(1, q - 1)
    pass
    v = w ** p % q
    r = n ** ((p + 1) // 2) % q
    while True:
    i = 0
    z = r * r * m % q
    while i < s and z % q != 1:
    i = i + 1
    z = z * z
    pass
    if i == 0:
    return (r, q - r)
    p2 = s - i - 1
    if p2 < 0: raise Exception("not found")
    r = r * (v ** (1 << p2) % q) % q
    pass
    assert False, "not reached"
    pass


    def jacobi(a, q):
    """jacobi symbol: judge existing sqrtmod
    - j(a*b,q) = j(a,q)*j(b,q)
    - j(a*q+b, q) = j(b, q)
    - j(a, 1) = 1
    - j(0, q) = 0
    - j(2, q) = -1 ** (q^2 - 1)/8
    - j(p, q) = -1 ^ {(p - 1)/2} * -1 ^ {(q - 1)/2} * j(q, p)
    """
    if q == 1: return 1
    if a == 0: return 0
    if a % 2 == 0: return (-1) ** ((q * q - 1) // 8) * jacobi(a // 2, q)
    return (-1) ** ((a - 1) // 2) * (-1) ** ((q - 1) // 2) * jacobi(q % a, a)
  10. @bellbind bellbind revised this gist Dec 5, 2011. 1 changed file with 25 additions and 5 deletions.
    30 changes: 25 additions & 5 deletions ecc.py
    Original file line number Diff line number Diff line change
    @@ -6,12 +6,32 @@ def inv(n, q):
    """div on PN modulo a/b mod q as a * inv(b, q) mod q
    >>> assert n * inv(n, q) % q == 1
    """
    for i in range(q):
    if (n * i) % q == 1:
    return i
    # n*inv % q = 1 => n*inv = q*m + 1 => n*inv + q*-m = 1
    # => egcd(n, q) = (inv, -m, 1) => inv = egcd(n, q)[0] (mod q)
    return egcd(n, q)[0] % q
    #[ref] naive implementation
    #for i in range(q):
    # if (n * i) % q == 1:
    # return i
    # pass
    #assert False, "unreached"
    #pass


    def egcd(a, b):
    """extended GCD
    returns: (s, t, gcd) as a*s + b*t == gcd
    >>> s, t, gcd = egcd(a, b)
    >>> assert a % gcd == 0 and b % gcd == 0
    >>> assert a * s + b * t == gcd
    """
    s0, s1, t0, t1 = 1, 0, 0, 1
    while b > 0:
    q, r = divmod(a, b)
    a, b = b, r
    s0, s1, t0, t1 = s1, s0 - q * s1, t1, t0 - q * t1
    pass
    assert False, "unreached"
    pass
    return s0, t0, a


    def sqrt(n, q):
  11. @bellbind bellbind revised this gist Dec 5, 2011. 1 changed file with 6 additions and 4 deletions.
    10 changes: 6 additions & 4 deletions ecc.py
    Original file line number Diff line number Diff line change
    @@ -75,10 +75,12 @@ def neg(self, p):

    def add(self, p1, p2):
    """<add> of elliptic curve: negate of 3rd cross point of (p1,p2) line
    >>> c = ec.add(a, b)
    >>> assert ec.is_valid(c)
    >>> assert ec.add(c, ec.neg(b)) == a
    >>> d = ec.add(a, b)
    >>> assert ec.is_valid(d)
    >>> assert ec.add(d, ec.neg(b)) == a
    >>> assert ec.add(a, ec.neg(a)) == ec.zero
    >>> assert ec.add(a, b) == ec.add(b, a)
    >>> assert ec.add(a, ec.add(b, c)) == ec.add(ec.add(a, b), c)
    """
    if p1 == self.zero: return p2
    if p2 == self.zero: return p1
    @@ -100,7 +102,7 @@ def mul(self, p, n):
    """n times <mul> of elliptic curve
    >>> m = ec.mul(p, n)
    >>> assert ec.is_valid(m)
    >>> assert ec.mul(p, ec.q) == ec.zero
    >>> assert ec.mul(p, 0) == ec.zero
    """
    r = self.zero
    m2 = p
  12. @bellbind bellbind revised this gist Dec 5, 2011. 1 changed file with 10 additions and 4 deletions.
    14 changes: 10 additions & 4 deletions ecc.py
    Original file line number Diff line number Diff line change
    @@ -118,8 +118,13 @@ def mul(self, p, n):
    return r

    def order(self, g):
    assert self.is_valid(g)
    for i in range(2, self.q + 1):
    """order of point g
    >>> o = ec.order(g)
    >>> assert ec.is_valid(a) and ec.mul(a, o) == ec.zero
    >>> assert o <= ec.q
    """
    assert self.is_valid(g) and g != self.zero
    for i in range(1, self.q + 1):
    if self.mul(g, i) == self.zero:
    return i
    pass
    @@ -137,6 +142,7 @@ def __init__(self, ec, g):
    assert ec.is_valid(g)
    self.ec = ec
    self.g = g
    self.n = ec.order(g)
    pass

    def gen(self, priv):
    @@ -243,14 +249,14 @@ def validate(self, hashval, sig, pub):
    # shared elliptic curve system of examples
    ec = EC(1, 18, 19)
    g, _ = ec.at(7)

    assert ec.order(g) <= ec.q

    # ElGamal enc/dec usage
    eg = ElGamal(ec, g)
    # mapping value to ec point
    # "masking": value k to point ec.mul(g, k)
    # ("imbedding" on proper n:use a point of x as 0 <= n*v <= x < n*(v+1) < q)
    mapping = [ec.mul(g, i) for i in range(ec.q)]
    mapping = [ec.mul(g, i) for i in range(eg.n)]
    plain = mapping[7]

    priv = 5
  13. @bellbind bellbind revised this gist Dec 5, 2011. 1 changed file with 22 additions and 10 deletions.
    32 changes: 22 additions & 10 deletions ecc.py
    Original file line number Diff line number Diff line change
    @@ -116,6 +116,14 @@ def mul(self, p, n):
    # r = self.add(r, p)
    # pass
    return r

    def order(self, g):
    assert self.is_valid(g)
    for i in range(2, self.q + 1):
    if self.mul(g, i) == self.zero:
    return i
    pass
    raise Exception("Invalid order")
    pass


    @@ -167,13 +175,14 @@ class DiffieHellman(object):
    - g: a point on ec
    """
    def __init__(self, ec, g):
    assert ec.is_valid(g)
    self.ec = ec
    self.g = g
    self.n = ec.order(g)
    pass

    def gen(self, priv):
    """generate pub key"""
    assert 0 < priv and priv < self.n
    return self.ec.mul(self.g, priv)

    def secret(self, priv, pub):
    @@ -183,23 +192,25 @@ def secret(self, priv, pub):
    - returns: shared secret as a point on ec
    """
    assert self.ec.is_valid(pub)
    assert self.ec.mul(pub, self.n) == self.ec.zero
    return self.ec.mul(pub, priv)
    pass


    class DSA(object):
    """(Simplified) ECDSA
    """ECDSA
    - ec: elliptic curve
    - g: a point on ec
    """
    def __init__(self, ec, g):
    assert ec.is_valid(g)
    self.ec = ec
    self.g = g
    self.n = ec.order(g)
    pass

    def gen(self, priv):
    """generate pub key"""
    assert 0 < priv and priv < self.n
    return self.ec.mul(self.g, priv)

    def sign(self, hashval, priv, r):
    @@ -209,9 +220,9 @@ def sign(self, hashval, priv, r):
    - r: random int
    - returns: signature as (int, int)
    """
    assert 0 < r and r < self.ec.q
    assert 0 < r and r < self.n
    m = self.ec.mul(self.g, r)
    return (m.x, inv(r, self.ec.q) * (hashval + m.x * priv) % self.ec.q)
    return (m.x, inv(r, self.n) * (hashval + m.x * priv) % self.n)

    def validate(self, hashval, sig, pub):
    """validate signature
    @@ -220,10 +231,11 @@ def validate(self, hashval, sig, pub):
    - pub: pub key as a point on ec
    """
    assert self.ec.is_valid(pub)
    w = inv(sig[1], self.ec.q)
    u1, u2 = hashval * w % self.ec.q, sig[0] * w % self.ec.q
    assert self.ec.mul(pub, self.n) == self.ec.zero
    w = inv(sig[1], self.n)
    u1, u2 = hashval * w % self.n, sig[0] * w % self.n
    p = self.ec.add(self.ec.mul(self.g, u1), self.ec.mul(pub, u2))
    return p.x == sig[0]
    return p.x % self.n == sig[0]
    pass


    @@ -253,7 +265,7 @@ def validate(self, hashval, sig, pub):
    # ECDH usage
    dh = DiffieHellman(ec, g)

    apriv = 15
    apriv = 11
    apub = dh.gen(apriv)

    bpriv = 3
    @@ -278,7 +290,7 @@ def validate(self, hashval, sig, pub):
    priv = 11
    pub = eg.gen(priv)
    hashval = 128
    r = 17
    r = 7

    sig = dsa.sign(hashval, priv, r)
    assert dsa.validate(hashval, sig, pub)
  14. @bellbind bellbind revised this gist Dec 4, 2011. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions ecc.py
    Original file line number Diff line number Diff line change
    @@ -82,7 +82,7 @@ def add(self, p1, p2):
    """
    if p1 == self.zero: return p2
    if p2 == self.zero: return p1
    if p1.x == p2.x and p1.y != p2.y:
    if p1.x == p2.x and (p1.y != p2.y or p1.y == 0):
    # p1 + -p1 == 0
    return self.zero
    if p1.x == p2.x:
    @@ -222,7 +222,7 @@ def validate(self, hashval, sig, pub):
    assert self.ec.is_valid(pub)
    w = inv(sig[1], self.ec.q)
    u1, u2 = hashval * w % self.ec.q, sig[0] * w % self.ec.q
    p = self.ec.add(self.ec.mul(g, u1), self.ec.mul(pub, u2))
    p = self.ec.add(self.ec.mul(self.g, u1), self.ec.mul(pub, u2))
    return p.x == sig[0]
    pass

  15. @bellbind bellbind revised this gist Dec 4, 2011. 1 changed file with 11 additions and 3 deletions.
    14 changes: 11 additions & 3 deletions ecc.py
    Original file line number Diff line number Diff line change
    @@ -76,8 +76,9 @@ def neg(self, p):
    def add(self, p1, p2):
    """<add> of elliptic curve: negate of 3rd cross point of (p1,p2) line
    >>> c = ec.add(a, b)
    >>> assert ec.is_valid(a)
    >>> assert ec.is_valid(c)
    >>> assert ec.add(c, ec.neg(b)) == a
    >>> assert ec.add(a, ec.neg(a)) == ec.zero
    """
    if p1 == self.zero: return p2
    if p2 == self.zero: return p1
    @@ -97,8 +98,9 @@ def add(self, p1, p2):

    def mul(self, p, n):
    """n times <mul> of elliptic curve
    >>> m = ec.mul(n, p)
    >>> m = ec.mul(p, n)
    >>> assert ec.is_valid(m)
    >>> assert ec.mul(p, ec.q) == ec.zero
    """
    r = self.zero
    m2 = p
    @@ -175,14 +177,20 @@ def gen(self, priv):
    return self.ec.mul(self.g, priv)

    def secret(self, priv, pub):
    """calc shared secret key for the pair"""
    """calc shared secret key for the pair
    - priv: my private key as int
    - pub: partner pub key as a point on ec
    - returns: shared secret as a point on ec
    """
    assert self.ec.is_valid(pub)
    return self.ec.mul(pub, priv)
    pass


    class DSA(object):
    """(Simplified) ECDSA
    - ec: elliptic curve
    - g: a point on ec
    """
    def __init__(self, ec, g):
    assert ec.is_valid(g)
  16. @bellbind bellbind revised this gist Dec 4, 2011. 1 changed file with 50 additions and 0 deletions.
    50 changes: 50 additions & 0 deletions ecc.py
    Original file line number Diff line number Diff line change
    @@ -181,6 +181,44 @@ def secret(self, priv, pub):
    pass


    class DSA(object):
    """(Simplified) ECDSA
    """
    def __init__(self, ec, g):
    assert ec.is_valid(g)
    self.ec = ec
    self.g = g
    pass

    def gen(self, priv):
    """generate pub key"""
    return self.ec.mul(self.g, priv)

    def sign(self, hashval, priv, r):
    """generate signature
    - hashval: hash value of message as int
    - priv: priv key as int
    - r: random int
    - returns: signature as (int, int)
    """
    assert 0 < r and r < self.ec.q
    m = self.ec.mul(self.g, r)
    return (m.x, inv(r, self.ec.q) * (hashval + m.x * priv) % self.ec.q)

    def validate(self, hashval, sig, pub):
    """validate signature
    - hashval: hash value of message as int
    - sig: signature as (int, int)
    - pub: pub key as a point on ec
    """
    assert self.ec.is_valid(pub)
    w = inv(sig[1], self.ec.q)
    u1, u2 = hashval * w % self.ec.q, sig[0] * w % self.ec.q
    p = self.ec.add(self.ec.mul(g, u1), self.ec.mul(pub, u2))
    return p.x == sig[0]
    pass


    if __name__ == "__main__":
    # shared elliptic curve system of examples
    ec = EC(1, 18, 19)
    @@ -224,4 +262,16 @@ def secret(self, priv, pub):
    assert dh.secret(apriv, cpub) != dh.secret(apriv, bpub)
    assert dh.secret(bpriv, apub) != dh.secret(bpriv, cpub)
    assert dh.secret(cpriv, bpub) != dh.secret(cpriv, apub)


    # ECDSA usage
    dsa = DSA(ec, g)

    priv = 11
    pub = eg.gen(priv)
    hashval = 128
    r = 17

    sig = dsa.sign(hashval, priv, r)
    assert dsa.validate(hashval, sig, pub)
    pass
  17. @bellbind bellbind revised this gist Dec 4, 2011. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions ecc.py
    Original file line number Diff line number Diff line change
    @@ -192,8 +192,8 @@ def secret(self, priv, pub):
    # mapping value to ec point
    # "masking": value k to point ec.mul(g, k)
    # ("imbedding" on proper n:use a point of x as 0 <= n*v <= x < n*(v+1) < q)
    plains = [ec.mul(g, i) for i in range(ec.q)]
    plain = plains[7]
    mapping = [ec.mul(g, i) for i in range(ec.q)]
    plain = mapping[7]

    priv = 5
    pub = eg.gen(priv)
  18. @bellbind bellbind revised this gist Dec 4, 2011. 1 changed file with 35 additions and 21 deletions.
    56 changes: 35 additions & 21 deletions ecc.py
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    # Basics of Elliptic Curve Cryptography
    # Basics of Elliptic Curve Cryptography implementation on Python
    import collections


    @@ -101,9 +101,18 @@ def mul(self, p, n):
    >>> assert ec.is_valid(m)
    """
    r = self.zero
    for i in range(n):
    r = self.add(r, p)
    m2 = p
    # O(log2(n)) add
    while 0 < n:
    if n & 1 == 1:
    r = self.add(r, m2)
    pass
    n, m2 = n >> 1, self.add(m2, m2)
    pass
    # [ref] O(n) add
    #for i in range(n):
    # r = self.add(r, p)
    # pass
    return r
    pass

    @@ -135,21 +144,22 @@ def enc(self, plain, pub, r):
    - returns: (cipher1, ciper2) as points on ec
    """
    assert self.ec.is_valid(plain)
    return (self.ec.mul(g, r),
    self.ec.add(plain, self.ec.mul(pub, r)))
    assert self.ec.is_valid(pub)
    return (self.ec.mul(g, r), self.ec.add(plain, self.ec.mul(pub, r)))

    def dec(self, cipher, priv):
    """decrypt
    - chiper: (chiper1, chiper2) as points on ec
    - priv: private key as int < ec.q
    - returns: plain as a point on ec
    """
    return self.ec.add(cipher[1],
    self.ec.neg(self.ec.mul(cipher[0], priv)))
    c1, c2 = cipher
    assert self.ec.is_valid(c1) and ec.is_valid(c2)
    return self.ec.add(c2, self.ec.neg(self.ec.mul(c1, priv)))
    pass


    class ECDH(object):
    class DiffieHellman(object):
    """Elliptic Curve Diffie Hellman (Key Agreement)
    - ec: elliptic curve
    - g: a point on ec
    @@ -172,42 +182,46 @@ def secret(self, priv, pub):


    if __name__ == "__main__":
    # enc/dec usage
    # shared elliptic curve system of examples
    ec = EC(1, 18, 19)
    g, _ = ec.at(7)


    # ElGamal enc/dec usage
    eg = ElGamal(ec, g)
    priv = 5
    # mapping value to ec point
    # "masking": value k to point ec.mul(g, k)
    # ("imbedding" on proper n:use a point of x as 0 <= n*v <= x < n*(v+1) < q)
    plains = [ec.mul(g, i) for i in range(ec.q)]
    plain = plains[7]

    priv = 5
    pub = eg.gen(priv)

    cipher = eg.enc(plain, pub, 15)
    decoded = eg.dec(cipher, priv)
    assert decoded == plain
    assert cipher != pub


    # ecdh usage
    ecdh = ECDH(ec, g)
    # ECDH usage
    dh = DiffieHellman(ec, g)

    apriv = 15
    apub = ecdh.gen(apriv)
    apub = dh.gen(apriv)

    bpriv = 3
    bpub = ecdh.gen(bpriv)
    bpub = dh.gen(bpriv)

    cpriv = 7
    cpub = ecdh.gen(cpriv)
    cpub = dh.gen(cpriv)
    # same secret on each pair
    assert ecdh.secret(apriv, bpub) == ecdh.secret(bpriv, apub)
    assert ecdh.secret(apriv, cpub) == ecdh.secret(cpriv, apub)
    assert ecdh.secret(bpriv, cpub) == ecdh.secret(cpriv, bpub)
    assert dh.secret(apriv, bpub) == dh.secret(bpriv, apub)
    assert dh.secret(apriv, cpub) == dh.secret(cpriv, apub)
    assert dh.secret(bpriv, cpub) == dh.secret(cpriv, bpub)

    # not same secret on other pair
    assert ecdh.secret(apriv, cpub) != ecdh.secret(apriv, bpub)
    assert ecdh.secret(bpriv, apub) != ecdh.secret(bpriv, cpub)
    assert ecdh.secret(cpriv, bpub) != ecdh.secret(cpriv, apub)
    assert dh.secret(apriv, cpub) != dh.secret(apriv, bpub)
    assert dh.secret(bpriv, apub) != dh.secret(bpriv, cpub)
    assert dh.secret(cpriv, bpub) != dh.secret(cpriv, apub)
    pass
  19. @bellbind bellbind revised this gist Dec 4, 2011. 1 changed file with 24 additions and 15 deletions.
    39 changes: 24 additions & 15 deletions ecc.py
    Original file line number Diff line number Diff line change
    @@ -111,30 +111,32 @@ def mul(self, p, n):
    class ElGamal(object):
    """ElGamal Encryption
    pub key encryption as replacing (mulmod, powmod) to (ec.add, ec.mul)
    - ec: elliptic curve
    - g: (random) a point on ec
    """
    def __init__(self, ec):
    def __init__(self, ec, g):
    assert ec.is_valid(g)
    self.ec = ec
    self.g = g
    pass

    def gen(self, priv, pub1):
    def gen(self, priv):
    """generate pub key
    - priv: priv key as (random) int < ec.q
    - pub1: (random) a point on ec
    - returns: pub key as (pub1, pub2) as points on ec
    - returns: pub key as points on ec
    """
    assert self.ec.is_valid(pub1)
    return (pub1, self.ec.mul(pub1, priv))
    return self.ec.mul(g, priv)

    def enc(self, plain, pub, r):
    """encrypt
    - plain: data as a point on ec
    - pub: pubkey as (pub1, pub2) as points on ec
    - pub: pub key as points on ec
    - r: randam int < ec.q
    - returns: (cipher1, ciper2) as points on ec
    """
    assert self.ec.is_valid(plain)
    return (self.ec.mul(pub[0], r),
    self.ec.add(plain, self.ec.mul(pub[1], r)))
    return (self.ec.mul(g, r),
    self.ec.add(plain, self.ec.mul(pub, r)))

    def dec(self, cipher, priv):
    """decrypt
    @@ -149,18 +151,21 @@ def dec(self, cipher, priv):

    class ECDH(object):
    """Elliptic Curve Diffie Hellman (Key Agreement)
    - ec: elliptic curve
    - g: a point on ec
    """
    def __init__(self, ec, g):
    assert ec.is_valid(g)
    self.ec = ec
    self.g = g
    pass

    def gen(self, priv):
    """generate pub key"""
    return self.ec.mul(self.g, priv)

    def secret(self, priv, pub):
    """calc secret key for the pair"""
    """calc shared secret key for the pair"""
    assert self.ec.is_valid(pub)
    return self.ec.mul(pub, priv)
    pass
    @@ -169,19 +174,23 @@ def secret(self, priv, pub):
    if __name__ == "__main__":
    # enc/dec usage
    ec = EC(1, 18, 19)
    eg = ElGamal(ec)
    g, _ = ec.at(7)
    eg = ElGamal(ec, g)
    priv = 5
    pub1, _ = ec.at(7)
    plain, _ = ec.at(1)
    # mapping value to ec point
    # "masking": value k to point ec.mul(g, k)
    # ("imbedding" on proper n:use a point of x as 0 <= n*v <= x < n*(v+1) < q)
    plains = [ec.mul(g, i) for i in range(ec.q)]
    plain = plains[7]

    pub = eg.gen(priv, pub1)
    pub = eg.gen(priv)
    cipher = eg.enc(plain, pub, 15)
    decoded = eg.dec(cipher, priv)
    assert decoded == plain
    assert cipher != pub


    # ecdh usage
    g, _ = ec.at(7) # shared
    ecdh = ECDH(ec, g)

    apriv = 15
  20. @bellbind bellbind revised this gist Dec 1, 2011. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion ecc.py
    Original file line number Diff line number Diff line change
    @@ -109,7 +109,7 @@ def mul(self, p, n):


    class ElGamal(object):
    """El Gamal Encryption
    """ElGamal Encryption
    pub key encryption as replacing (mulmod, powmod) to (ec.add, ec.mul)
    """
    def __init__(self, ec):
    @@ -146,6 +146,7 @@ def dec(self, cipher, priv):
    self.ec.neg(self.ec.mul(cipher[0], priv)))
    pass


    class ECDH(object):
    """Elliptic Curve Diffie Hellman (Key Agreement)
    """
  21. @bellbind bellbind revised this gist Dec 1, 2011. 1 changed file with 3 additions and 2 deletions.
    5 changes: 3 additions & 2 deletions ecc.py
    Original file line number Diff line number Diff line change
    @@ -15,8 +15,9 @@ def inv(n, q):


    def sqrt(n, q):
    """sqrt on PN modulo: it may not exist
    >>> assert (sqrt(n, q) ** 2) % q == n
    """sqrt on PN modulo: returns two numbers or exception if not exist
    >>> assert (sqrt(n, q)[0] ** 2) % q == n
    >>> assert (sqrt(n, q)[1] ** 2) % q == n
    """
    assert n < q
    for i in range(1, q):
  22. @bellbind bellbind revised this gist Dec 1, 2011. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion ecc.py
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,5 @@
    # Basics of Elliptic Curve Cryptography
    import collections
    import fractions


    def inv(n, q):
  23. @bellbind bellbind created this gist Dec 1, 2011.
    203 changes: 203 additions & 0 deletions ecc.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,203 @@
    # Basics of Elliptic Curve Cryptography
    import collections
    import fractions


    def inv(n, q):
    """div on PN modulo a/b mod q as a * inv(b, q) mod q
    >>> assert n * inv(n, q) % q == 1
    """
    for i in range(q):
    if (n * i) % q == 1:
    return i
    pass
    assert False, "unreached"
    pass


    def sqrt(n, q):
    """sqrt on PN modulo: it may not exist
    >>> assert (sqrt(n, q) ** 2) % q == n
    """
    assert n < q
    for i in range(1, q):
    if i * i % q == n:
    return (i, q - i)
    pass
    raise Exception("not found")


    Coord = collections.namedtuple("Coord", ["x", "y"])


    class EC(object):
    """System of Elliptic Curve"""
    def __init__(self, a, b, q):
    """elliptic curve as: (y**2 = x**3 + a * x + b) mod q
    - a, b: params of curve formula
    - q: prime number
    """
    assert 0 < a and a < q and 0 < b and b < q and q > 2
    assert (4 * (a ** 3) + 27 * (b ** 2)) % q != 0
    self.a = a
    self.b = b
    self.q = q
    # just as unique ZERO value representation for "add": (not on curve)
    self.zero = Coord(0, 0)
    pass

    def is_valid(self, p):
    if p == self.zero: return True
    l = (p.y ** 2) % self.q
    r = ((p.x ** 3) + self.a * p.x + self.b) % self.q
    return l == r

    def at(self, x):
    """find points on curve at x
    - x: int < q
    - returns: ((x, y), (x,-y)) or not found exception
    >>> a, ma = ec.at(x)
    >>> assert a.x == ma.x and a.x == x
    >>> assert a.x == ma.x and a.x == x
    >>> assert ec.neg(a) == ma
    >>> assert ec.is_valid(a) and ec.is_valid(ma)
    """
    assert x < self.q
    ysq = (x ** 3 + self.a * x + self.b) % self.q
    y, my = sqrt(ysq, self.q)
    return Coord(x, y), Coord(x, my)

    def neg(self, p):
    """negate p
    >>> assert ec.is_valid(ec.neg(p))
    """
    return Coord(p.x, -p.y % self.q)

    def add(self, p1, p2):
    """<add> of elliptic curve: negate of 3rd cross point of (p1,p2) line
    >>> c = ec.add(a, b)
    >>> assert ec.is_valid(a)
    >>> assert ec.add(c, ec.neg(b)) == a
    """
    if p1 == self.zero: return p2
    if p2 == self.zero: return p1
    if p1.x == p2.x and p1.y != p2.y:
    # p1 + -p1 == 0
    return self.zero
    if p1.x == p2.x:
    # p1 + p1: use tangent line of p1 as (p1,p1) line
    l = (3 * p1.x * p1.x + self.a) * inv(2 * p1.y, self.q) % self.q
    pass
    else:
    l = (p2.y - p1.y) * inv(p2.x - p1.x, self.q) % self.q
    pass
    x = (l * l - p1.x - p2.x) % self.q
    y = (l * (p1.x - x) - p1.y) % self.q
    return Coord(x, y)

    def mul(self, p, n):
    """n times <mul> of elliptic curve
    >>> m = ec.mul(n, p)
    >>> assert ec.is_valid(m)
    """
    r = self.zero
    for i in range(n):
    r = self.add(r, p)
    pass
    return r
    pass


    class ElGamal(object):
    """El Gamal Encryption
    pub key encryption as replacing (mulmod, powmod) to (ec.add, ec.mul)
    """
    def __init__(self, ec):
    self.ec = ec
    pass

    def gen(self, priv, pub1):
    """generate pub key
    - priv: priv key as (random) int < ec.q
    - pub1: (random) a point on ec
    - returns: pub key as (pub1, pub2) as points on ec
    """
    assert self.ec.is_valid(pub1)
    return (pub1, self.ec.mul(pub1, priv))

    def enc(self, plain, pub, r):
    """encrypt
    - plain: data as a point on ec
    - pub: pubkey as (pub1, pub2) as points on ec
    - r: randam int < ec.q
    - returns: (cipher1, ciper2) as points on ec
    """
    assert self.ec.is_valid(plain)
    return (self.ec.mul(pub[0], r),
    self.ec.add(plain, self.ec.mul(pub[1], r)))

    def dec(self, cipher, priv):
    """decrypt
    - chiper: (chiper1, chiper2) as points on ec
    - priv: private key as int < ec.q
    - returns: plain as a point on ec
    """
    return self.ec.add(cipher[1],
    self.ec.neg(self.ec.mul(cipher[0], priv)))
    pass

    class ECDH(object):
    """Elliptic Curve Diffie Hellman (Key Agreement)
    """
    def __init__(self, ec, g):
    assert ec.is_valid(g)
    self.ec = ec
    pass

    def gen(self, priv):
    """generate pub key"""
    return self.ec.mul(self.g, priv)

    def secret(self, priv, pub):
    """calc secret key for the pair"""
    assert self.ec.is_valid(pub)
    return self.ec.mul(pub, priv)
    pass


    if __name__ == "__main__":
    # enc/dec usage
    ec = EC(1, 18, 19)
    eg = ElGamal(ec)
    priv = 5
    pub1, _ = ec.at(7)
    plain, _ = ec.at(1)

    pub = eg.gen(priv, pub1)
    cipher = eg.enc(plain, pub, 15)
    decoded = eg.dec(cipher, priv)
    assert decoded == plain
    assert cipher != pub

    # ecdh usage
    g, _ = ec.at(7) # shared
    ecdh = ECDH(ec, g)

    apriv = 15
    apub = ecdh.gen(apriv)

    bpriv = 3
    bpub = ecdh.gen(bpriv)

    cpriv = 7
    cpub = ecdh.gen(cpriv)
    # same secret on each pair
    assert ecdh.secret(apriv, bpub) == ecdh.secret(bpriv, apub)
    assert ecdh.secret(apriv, cpub) == ecdh.secret(cpriv, apub)
    assert ecdh.secret(bpriv, cpub) == ecdh.secret(cpriv, bpub)

    # not same secret on other pair
    assert ecdh.secret(apriv, cpub) != ecdh.secret(apriv, bpub)
    assert ecdh.secret(bpriv, apub) != ecdh.secret(bpriv, cpub)
    assert ecdh.secret(cpriv, bpub) != ecdh.secret(cpriv, apub)
    pass