def test_int_from_integer(): i = secrets.randbits(256 - 8) assert i == int_from_integer(i) assert i == int_from_integer(hex(i).upper()) assert i == int_from_integer(bin(i).upper()) assert i == int_from_integer(hex_string(i)) assert i == int_from_integer(i.to_bytes(32, "big"))
def test_int_from_integer() -> None: for i in ( secrets.randbits(256 - 8), 0x0B6CA75B7D3076C561958CCED813797F6D2275C7F42F3856D007D587769A90, ): assert i == int_from_integer(i) assert i == int_from_integer(" " + hex(i).upper()) assert -i == int_from_integer(hex(-i).upper() + " ") assert i == int_from_integer(hex_string(i)) assert i == int_from_integer(i.to_bytes(32, "big"))
def test_utils(self): b = bytes_from_octets(int_with_whitespaces) s = b.hex() # lower case, no spaces self.assertNotEqual(int_with_whitespaces, s) self.assertEqual(hash160(int_with_whitespaces), hash160(s)) self.assertEqual(hash256(int_with_whitespaces), hash256(s)) i = secrets.randbits(256) self.assertEqual(i, int_from_integer(i)) self.assertEqual(i, int_from_integer(i.to_bytes(32, "big"))) self.assertEqual(i, int_from_integer(hex(i)))
def __init__(self, p: Integer, a: Integer, b: Integer, G: Point) -> None: super().__init__(p, a, b) # 2. check that xG and yG are integers in the interval [0, p−1] # 4. Check that yG^2 = xG^3 + a*xG + b (mod p) if len(G) != 2: raise BTClibValueError("Generator must a be a sequence[int, int]") self.G = (int_from_integer(G[0]), int_from_integer(G[1])) if not self.is_on_curve(self.G): raise BTClibValueError("Generator is not on the curve") self.GJ = self.G[0], self.G[1], 1 # Jacobian coordinates
def multi_mult(scalars: Sequence[Integer], points: Sequence[Point], ec: Curve = secp256k1) -> Point: """Return the multi scalar multiplication u1*Q1 + ... + un*Qn. Use Bos-Coster's algorithm for efficient computation. """ if len(scalars) != len(points): err_msg = "mismatch between number of scalars and points: " err_msg += f"{len(scalars)} vs {len(points)}" raise BTClibValueError(err_msg) jac_points: List[JacPoint] = [] ints: List[int] = [] for Q, i in zip(points, scalars): i = int_from_integer(i) % ec.n if i == 0: # early optimization, even if not strictly necessary continue ints.append(i) ec.require_on_curve(Q) jac_points.append(jac_from_aff(Q)) R = _multi_mult(ints, jac_points, ec) return ec.aff_from_jac(R)
def double_mult(u: Integer, H: Point, v: Integer, Q: Point, ec: Curve = secp256k1) -> Point: "Double scalar multiplication (u*H + v*Q)." ec.require_on_curve(H) HJ = jac_from_aff(H) ec.require_on_curve(Q) QJ = jac_from_aff(Q) u = int_from_integer(u) % ec.n v = int_from_integer(v) % ec.n R = _double_mult(u, HJ, v, QJ, ec) return ec.aff_from_jac(R)
def __init__(self, p: Integer, a: Integer, b: Integer) -> None: # Parameters are checked according to SEC 1 v.2 3.1.1.2.1 p = int_from_integer(p) a = int_from_integer(a) b = int_from_integer(b) # 1) check that p is a prime # Fermat test will do as _probabilistic_ primality test... if p < 2 or p % 2 == 0 or pow(2, p - 1, p) != 1: err_msg = "p is not prime: " err_msg += f"'{hex_string(p)}'" if p > HEX_THRESHOLD else f"{p}" raise BTClibValueError(err_msg) plen = p.bit_length() # byte-length self.p_size = ceil(plen / 8) # must be true to break simmetry using quadratic residue self.p_is_3_mod_4 = p % 4 == 3 self.p = p # 2. check that a and b are integers in the interval [0, p−1] if a < 0: raise BTClibValueError(f"negative a: {a}") if p <= a: err_msg = "p <= a: " + (f"'{hex_string(p)}' <= '{hex_string(a)}'" if p > HEX_THRESHOLD else f"{p} <= {a}") raise BTClibValueError(err_msg) if b < 0: raise BTClibValueError(f"negative b: {b}") if p <= b: err_msg = "p <= b: " + (f"'{hex_string(p)}' <= '{hex_string(b)}'" if p > HEX_THRESHOLD else f"{p} <= {b}") raise BTClibValueError(err_msg) # 3. Check that 4*a^3 + 27*b^2 ≠ 0 (mod p) d = 4 * a * a * a + 27 * b * b if d % p == 0: raise BTClibValueError("zero discriminant") self._a = a self._b = b
def mult(m: Integer, Q: Optional[Point] = None, ec: Curve = secp256k1) -> Point: "Elliptic curve scalar multiplication." if Q is None: QJ = ec.GJ else: ec.require_on_curve(Q) QJ = jac_from_aff(Q) m = int_from_integer(m) % ec.n R = _mult(m, QJ, ec) return ec.aff_from_jac(R)
def __init__( self, p: Integer, a: Integer, b: Integer, G: Point, n: Integer, cofactor: int, weakness_check: bool = True, name: Optional[str] = None, ) -> None: super().__init__(p, a, b, G) n = int_from_integer(n) # Security level is expressed in bits, where n-bit security # means that the attacker would have to perform 2^n operations # to break it. Security bits are half the key size for asymmetric # elliptic curve cryptography, i.e. half of the number of bits # required to express the group order n or, holding Hasse theorem, # to express the field prime p self.n = n self.nlen = n.bit_length() self.n_size = (self.nlen + 7) // 8 # 5. Check that n is prime. if n < 2 or n % 2 == 0 or pow(2, n - 1, n) != 1: err_msg = "n is not prime: " err_msg += f"{hex_string(n)}" if n > HEX_THRESHOLD else f"{n}" raise BTClibValueError(err_msg) delta = int(2 * sqrt(self.p)) # also check n with Hasse Theorem if cofactor < 2 and not self.p + 1 - delta <= n <= self.p + 1 + delta: err_msg = "n not in p+1-delta..p+1+delta: " err_msg += f"{hex_string(n)}" if n > HEX_THRESHOLD else f"{n}" raise BTClibValueError(err_msg) # 7. Check that G ≠ INF, nG = INF if self.G[1] == 0: err_msg = "INF point cannot be a generator" raise BTClibValueError(err_msg) jac_inf = _mult(n, self.GJ, self) if jac_inf[2] != 0: err_msg = "n is not the group order: " err_msg += f"{hex_string(n)}" if n > HEX_THRESHOLD else f"{n}" raise BTClibValueError(err_msg) # 6. Check cofactor exp_cofactor = int(1 / n + delta / n + self.p / n) if cofactor != exp_cofactor: err_msg = f"invalid cofactor: {cofactor}, expected {exp_cofactor}" raise BTClibValueError(err_msg) self.cofactor = cofactor # 8. Check that n ≠ p if n == p: raise BTClibValueError( f"n=p weak curve: {hex_string(n)}") # pragma: no cover if weakness_check: # 8. Check that p^i % n ≠ 1 for all 1≤i<100 for i in range(1, 100): if pow(self.p, i, n) == 1: raise UserWarning("weak curve") self.name = name