def add(p: tuple, q: tuple) -> tuple or None: """Returns the result of p + q according to the group law.""" assert on_curve(p) assert on_curve(q) if p is None: # 0 + q = q return q if q is None: # p + 0 = p return p # # p == -q # if p == negative(q): return None # # p != -q # x1, y1 = p x2, y2 = q if p == q: m = (3 * x1 * x1 + curve.a) * modular_multiplicative_inverse( 2 * y1, curve.p) else: m = (y1 - y2) * modular_multiplicative_inverse(x1 - x2, curve.p) x = m * m - x1 - x2 y = y1 + m * (x - x1) result = (x % curve.p, -y % curve.p) assert on_curve(result) return result
def invss(self, a_shares: list, debug: bool = False) -> list: """Returns shares of modular multiplicative inverse of a, with shares of a, without knowing a""" assert len(a_shares) == self.group_size if debug: print('------------ invss ------------') print(ThresholdSignature.inspect(a_shares)) b, _ = self.jvrss(debug) u = self.pross(a_shares, b, debug) mod_inv_u = modular_multiplicative_inverse(u, curve.n) inverse_shares = [(mod_inv_u * bi) % curve.n for bi in b] if debug: print(f'u = {u}') print(f'mod_inv_u = {mod_inv_u}') print( f'inverse shares = {ThresholdSignature.inspect(inverse_shares)}' ) random_points = random.sample( ThresholdSignature.shares_to_points(inverse_shares), 2 * self.polynomial_order + 1) print( f'points picked = {ThresholdSignature.inspect(random_points)}') secret_inverse = Polynomial.interpolate_evaluate(random_points, 0) print(f'inverse secret = {secret_inverse}') print('-------------------------------') return inverse_shares
def verify_signature(public_key: tuple, message: bytes, signature: tuple) -> bool: """Verify signature with public key and message""" e = hash_to_int(message) r, s = signature w = modular_multiplicative_inverse(s, curve.n) u1 = (w * e) % curve.n u2 = (w * r) % curve.n x, _ = add(scalar_multiply(u1, curve.g), scalar_multiply(u2, public_key)) return r == (x % curve.n)
def sign(private_key: int, message: bytes) -> tuple: """Create ECDSA signature (r, s)""" e = hash_to_int(message) r, s = 0, 0 while not r or not s: k = random.randrange(1, curve.n) k_x, _ = scalar_multiply(k, curve.g) r = k_x % curve.n s = ((e + r * private_key) * modular_multiplicative_inverse(k, curve.n)) % curve.n return r, s
def sign_recoverable(private_key: int, message: bytes) -> tuple: """Create recoverable ECDSA signature, aka compact signature, (recovery_id, r, s)""" e = hash_to_int(message) recovery_id, r, s = 0, 0, 0 while not r or not s: k = random.randrange(1, curve.n) k_x, k_y = scalar_multiply(k, curve.g) # r r = k_x % curve.n recovery_id = 0 | 2 if k_x > curve.n else 0 | k_y % 2 # s s = ((e + r * private_key) * modular_multiplicative_inverse(k, curve.n)) % curve.n return recovery_id, r, s
def verify_message(p2pkh_address: str, plain_text: str, signature: str) -> bool: """Verify serialized compact signature with p2pkh address and plain text""" sig_bytes = b64decode(signature) if len(sig_bytes) != 65: return False prefix, r, s = sig_bytes[0], int.from_bytes( sig_bytes[1:33], byteorder='big'), int.from_bytes(sig_bytes[33:], byteorder='big') # Calculate recovery_id compressed = False if prefix < 27 or prefix >= 35: return False if prefix >= 31: compressed = True prefix -= 4 recovery_id = prefix - 27 # Recover point kG, k is the ephemeral private key x = r + (curve.n if recovery_id >= 2 else 0) y_squared = (x * x * x + curve.a * x + curve.b) % curve.p y = pow(y_squared, (curve.p + 1) // 4, curve.p) if (y + recovery_id) % 2 != 0: y = -y % curve.p point_k = (x, y) # Calculate point aG, a is the private key d = message_digest(plain_text) e = hash_to_int(d) mod_inv_r = modular_multiplicative_inverse(r, curve.n) public_key = add(scalar_multiply(mod_inv_r * s, point_k), scalar_multiply(mod_inv_r * (-e % curve.n), curve.g)) # Verify signature if not verify_signature(public_key, d, (r, s)): return False # Check public key hash if public_key_hash( public_key, compressed) != address_to_public_key_hash(p2pkh_address): return False # OK return True
def jvrss(self, debug: bool = False) -> tuple: """Returns (shares_of_participants, group_shared_public_key)""" if debug: print('------------ jvrss ------------') # Random polynomials for each player polynomials = [] for i in range(self.group_size): p = Polynomial.random(self.polynomial_order, debug) if debug: print(f'Player {i + 1} {p}') polynomials.append(p) # Calculate shares for each player shares = [0] * self.group_size for i in range(self.group_size): for j in range(self.group_size): fij = polynomials[i].evaluate(j + 1) shares[j] += fij if debug: print(f'f{i + 1}({j + 1}) = {fij}', end='\t') if debug: print() for i in range(len(shares)): shares[i] %= curve.n # Calculate group shared public key public_key = None for i in range(self.group_size): public_key = add( public_key, scalar_multiply(polynomials[i].coefficients[0], curve.g)) if debug: secret = sum([p.coefficients[0] for p in polynomials]) % curve.n mod_inv_secret = modular_multiplicative_inverse(secret, curve.n) print(f'secret = {secret}') print(f'mod_inv_secret = {mod_inv_secret}') print(f'public key = {public_key}') print(f'shares = {ThresholdSignature.inspect(shares)}') print('-------------------------------') return shares, public_key