def test_gec(self): """GEC 2: Test Vectors for SEC 1, section 2 http://read.pudn.com/downloads168/doc/772358/TestVectorsforSEC%201-gec2.pdf """ # 2.1.1 Scheme setup ec = secp160r1 hf = sha1 # 2.1.2 Key Deployment for U dU = 971761939728640320549601132085879836204587084162 self.assertEqual(format(dU, str(ec.psize) + 'x'), 'aa374ffc3ce144e6b073307972cb6d57b2a4e982') QU = mult(ec, dU) self.assertEqual(QU, (466448783855397898016055842232266600516272889280, 1110706324081757720403272427311003102474457754220)) self.assertEqual( octets_from_point(ec, QU, True).hex(), '0251b4496fecc406ed0e75a24a3c03206251419dc0') # 2.1.3 Signing Operation for U msg = b'abc' k = 702232148019446860144825009548118511996283736794 exp_sig = (0xCE2873E5BE449563391FEB47DDCBA2DC16379191, 0x3480EC1371A091A464B31CE47DF0CB8AA2D98B54) sig = dsa.sign(ec, hf, msg, dU, k) r, s = sig self.assertEqual(r, exp_sig[0]) self.assertIn(s, (exp_sig[1], ec.n - exp_sig[1])) # 2.1.4 Verifying Operation for V self.assertTrue(dsa.verify(ec, hf, msg, QU, sig)) self.assertTrue(dsa._verify(ec, hf, msg, QU, sig))
def test_rfc6979_tv() -> None: fname = "rfc6979.json" filename = path.join(path.dirname(__file__), "test_data", fname) with open(filename, "r") as f: test_dict = json.load(f) for ec_name in test_dict: ec = CURVES[ec_name] test_vectors = test_dict[ec_name] for x, x_U, y_U, hf, msg, k, r, s in test_vectors: x = int(x, 16) # test RFC6979 implementation k2 = rfc6979(msg, x, ec, eval("hashlib." + hf)) assert k == hex(k2) # test RFC6979 usage in DSA sig = dsa.sign(msg, x, k2, False, ec, eval("hashlib." + hf)) assert r == hex(sig[0]) assert s == hex(sig[1]) # test that RFC6979 is the default nonce for DSA sig = dsa.sign(msg, x, k=None, low_s=False, ec=ec, hf=eval("hashlib." + hf)) assert r == hex(sig[0]) assert s == hex(sig[1]) # test key-pair coherence U = mult(x, ec.G, ec) assert (int(x_U, 16), int(y_U, 16)) == U # test signature validity dsa.assert_as_valid(msg, U, sig, ec, hf=eval("hashlib." + hf))
def test_negate() -> None: for ec in all_curves.values(): # just a random point, not INF q = 1 + secrets.randbelow(ec.n - 1) Q = mult(q, ec.G, ec) minus_Q = ec.negate(Q) assert ec.add(Q, minus_Q) == INF # Jacobian coordinates QJ = _jac_from_aff(Q) minus_QJ = ec.negate_jac(QJ) assert ec._jac_equality(ec._add_jac(QJ, minus_QJ), INFJ) # negate of INF is INF minus_INF = ec.negate(INF) assert minus_INF == INF # negate of INFJ is INFJ minus_INFJ = ec.negate_jac(INFJ) assert ec._jac_equality(minus_INFJ, INFJ) with pytest.raises(TypeError, match="not a point"): ec.negate(ec.GJ) # type: ignore with pytest.raises(TypeError, match="not a Jacobian point"): ec.negate_jac(ec.G) # type: ignore
def test_crack_prvkey() -> None: ec = CURVES["secp256k1"] q = 0x19E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725 x_Q = mult(q)[0] msg1_str = "Paolo is afraid of ephemeral random numbers" msg1 = hf(msg1_str.encode()).digest() k, _ = ssa._det_nonce(msg1, q) sig1 = ssa._sign(msg1, q, k) msg2_str = "and Paolo is right to be afraid" msg2 = hf(msg2_str.encode()).digest() # reuse same k sig2 = ssa._sign(msg2, q, k) qc, kc = ssa._crack_prvkey(msg1, sig1, msg2, sig2, x_Q) assert q in (qc, ec.n - qc) assert k in (kc, ec.n - kc) with pytest.raises(ValueError, match="not the same r in signatures"): ssa._crack_prvkey(msg1, sig1, msg2, (16, sig1[1]), x_Q) with pytest.raises(ValueError, match="identical signatures"): ssa._crack_prvkey(msg1, sig1, msg1, sig1, x_Q)
def test_point_from_bip340pubkey() -> None: q, x_Q = ssa.gen_keys() P = mult(q) # Integer (int) assert ssa.point_from_bip340pubkey(x_Q) == P # Integer (bytes) assert ssa.point_from_bip340pubkey(x_Q.to_bytes(32, byteorder="big")) == P # Integer (hex-str) assert ssa.point_from_bip340pubkey( x_Q.to_bytes(32, byteorder="big").hex()) == P # tuple Point assert ssa.point_from_bip340pubkey(P) == P # 33 bytes assert ssa.point_from_bip340pubkey(bytes_from_point(P)) == P # 33 bytes hex-string assert ssa.point_from_bip340pubkey(bytes_from_point(P).hex()) == P # 65 bytes assert ssa.point_from_bip340pubkey(bytes_from_point(P, compressed=False)) == P # 65 bytes hex-string assert ssa.point_from_bip340pubkey( bytes_from_point(P, compressed=False).hex()) == P xpub_data = BIP32KeyData.deserialize( "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy" ) xpub_data.key = bytes_from_point(P) # BIP32KeyData assert ssa.point_from_bip340pubkey(xpub_data) == P # BIP32Key encoded str xpub = xpub_data.serialize() assert ssa.point_from_bip340pubkey(xpub) == P # BIP32Key str assert ssa.point_from_bip340pubkey(xpub.decode("ascii")) == P
def test_Add(self): for ec in all_curves: Q1 = mult(ec, ec._p, ec.G) # just a random point, not Inf Q1J = _jac_from_aff(Q1) # distinct points Q3 = ec._add_aff(Q1, ec.G) Q3jac = ec._add_jac(Q1J, ec.GJ) self.assertEqual(Q3, ec._aff_from_jac(Q3jac)) # point at infinity Q3 = ec._add_aff(ec.G, Inf) Q3jac = ec._add_jac(ec.GJ, InfJ) self.assertEqual(Q3, ec._aff_from_jac(Q3jac)) Q3 = ec._add_aff(Inf, ec.G) Q3jac = ec._add_jac(InfJ, ec.GJ) self.assertEqual(Q3, ec._aff_from_jac(Q3jac)) # point doubling Q3 = ec._add_aff(Q1, Q1) Q3jac = ec._add_jac(Q1J, Q1J) self.assertEqual(Q3, ec._aff_from_jac(Q3jac)) # opposite points Q1opp = ec.opposite(Q1) Q3 = ec._add_aff(Q1, Q1opp) Q3jac = ec._add_jac(Q1J, _jac_from_aff(Q1opp)) self.assertEqual(Q3, ec._aff_from_jac(Q3jac))
def test_key_deployment(self): """ GEC 2: Test Vectors for SEC 1, section 4.1 http://read.pudn.com/downloads168/doc/772358/TestVectorsforSEC%201-gec2.pdf """ # 4.1.1 # ec = secp160r1 # hf = sha1 # 4.1.2 dU = 971761939728640320549601132085879836204587084162 self.assertEqual(format(dU, str(ec.psize)+'x'), 'aa374ffc3ce144e6b073307972cb6d57b2a4e982') QU = mult(ec, dU, ec.G) self.assertEqual(QU, (466448783855397898016055842232266600516272889280, 1110706324081757720403272427311003102474457754220)) self.assertEqual(octets_from_point(ec, QU, True).hex(), '0251b4496fecc406ed0e75a24a3c03206251419dc0') # 4.1.3 dV = 399525573676508631577122671218044116107572676710 self.assertEqual(format(dV, str(ec.psize)+'x'), '45fb58a92a17ad4b15101c66e74f277e2b460866') QV = mult(ec, dV, ec.G) self.assertEqual(QV, (420773078745784176406965940076771545932416607676, 221937774842090227911893783570676792435918278531)) self.assertEqual(octets_from_point(ec, QV, True).hex(), '0349b41e0e9c0369c2328739d90f63d56707c6e5bc') # expected results z_exp = 1155982782519895915997745984453282631351432623114 zstr = 'ca7c0f8c3ffa87a96e1b74ac8e6af594347bb40a' keydatasize = 20 keying_data_exp = '744ab703f5bc082e59185f6d049d2d367db245c2' # 4.1.4 z, _ = mult(ec, dU, QV) self.assertEqual(z, z_exp) self.assertEqual(format(z, str(ec.psize)+'x'), zstr) keyingdata = dh.kdf(octets_from_int(z, ec.psize), keydatasize, ec, hf) self.assertEqual(keyingdata.hex(), keying_data_exp) # 4.1.5 z, _ = mult(ec, dV, QU) self.assertEqual(z, z_exp) self.assertEqual(format(z, str(ec.psize)+'x'), zstr) keyingdata = dh.kdf(octets_from_int(z, ec.psize), keydatasize, ec, hf) self.assertEqual(keyingdata.hex(), keying_data_exp)
def test_aff_jac_conversions(self): for ec in all_curves: Q = mult(ec, ec._p, ec.G) # random point checkQ = ec._aff_from_jac(_jac_from_aff(Q)) self.assertEqual(Q, checkQ) # with only the last curve checkInf = ec._aff_from_jac(_jac_from_aff(Inf)) self.assertEqual(Inf, checkInf)
def test_all_curves(self): for ec in all_curves: self.assertEqual(mult(ec, 0, ec.G), Inf) self.assertEqual(mult(ec, 0, ec.G), Inf) self.assertEqual(mult(ec, 1, ec.G), ec.G) self.assertEqual(mult(ec, 1, ec.G), ec.G) Gy_odd = ec.y_odd(ec.G[0], True) self.assertEqual(Gy_odd % 2, 1) Gy_even = ec.y_odd(ec.G[0], False) self.assertEqual(Gy_even % 2, 0) self.assertTrue(ec.G[1] in (Gy_odd, Gy_even)) Gbytes = octets_from_point(ec, ec.G, True) G2 = point_from_octets(ec, Gbytes) self.assertEqual(ec.G, G2) Gbytes = octets_from_point(ec, ec.G, False) G2 = point_from_octets(ec, Gbytes) self.assertEqual(ec.G, G2) P = ec.add(Inf, ec.G) self.assertEqual(P, ec.G) P = ec.add(ec.G, Inf) self.assertEqual(P, ec.G) P = ec.add(Inf, Inf) self.assertEqual(P, Inf) P = ec.add(ec.G, ec.G) self.assertEqual(P, mult(ec, 2, ec.G)) P = mult(ec, ec.n-1, ec.G) self.assertEqual(ec.add(P, ec.G), Inf) self.assertEqual(mult(ec, ec.n, ec.G), Inf) self.assertEqual(mult(ec, 0, Inf), Inf) self.assertEqual(mult(ec, 1, Inf), Inf) self.assertEqual(mult(ec, 25, Inf), Inf) ec_repr = repr(ec) if ec in low_card_curves or ec.psize < 24: ec_repr = ec_repr[:-1] + ", False)" ec2 = eval(ec_repr) self.assertEqual(str(ec), str(ec2))
def pubkey_bytes_from_prvkey(prvkey, compressed=True): PubKey = mult(ec, prvkey, ec.G) if compressed: prefix = b'\x02' if (PubKey[1] % 2 == 0) else b'\x03' return prefix + PubKey[0].to_bytes(32, byteorder='big') else: prefix = b'\x04' return prefix + PubKey[0].to_bytes(32, byteorder='big') + \ PubKey[1].to_bytes(32, byteorder='big')
def pubkey_bytes_from_prvkey(prvkey, compressed=True): PubKey = mult(prvkey) if compressed: prefix = b"\x02" if (PubKey[1] % 2 == 0) else b"\x03" return prefix + PubKey[0].to_bytes(32, byteorder="big") else: prefix = b"\x04" return (prefix + PubKey[0].to_bytes(32, byteorder="big") + PubKey[1].to_bytes(32, byteorder="big"))
def key_agreement(dUV: int, QVU: Point, keydatasize: int, ec: Curve, hf) -> bytes: P = mult(ec, dUV, QVU) if P[1] == 0: "invalid (zero) private key" z = P[0] zbytes = octets_from_int(z, ec.psize) k = kdf(zbytes, keydatasize, ec, hf) return k
def _tweak(c: bytes, ec: Curve, hf, k: int) -> Tuple[Point, int]: """tweak kG returns: - point kG to tweak - tweaked private key k + h(kG||c), the corresponding pubkey is a commitment to kG, c """ R = mult(ec, k, ec.G) e = hf(octets_from_point(ec, R, True) + c).digest() e = int.from_bytes(e, 'big') return R, (e + k) % ec.n
def test_low_cardinality(self): """test all msg/key pairs of low cardinality elliptic curves""" # ec.n has to be prime to sign prime = [11, 13, 17, 19] # all possible hashed messages hsize = 32 H = [i.to_bytes(hsize, 'big') for i in range(max(prime) * 2)] # only low card curves or it would take forever for ec in low_card_curves: if ec._p in prime: # only few curves or it would take too long # Schnorr-bip only applies to curve whose prime p = 3 %4 if not ec.pIsThreeModFour: self.assertRaises(ValueError, ssa.sign, ec, hf, H[0], 1, None) continue for q in range(ec.n): # all possible private keys if q == 0: # invalid prvkey=0 self.assertRaises(ValueError, ssa.sign, ec, hf, H[0], q, None) self.assertRaises(ValueError, rfc6979, ec, hf, H[0], q) continue Q = mult(ec, q, ec.G) # public key for h in H: # all possible hashed messages # k = 0 self.assertRaises(ValueError, ssa.sign, ec, hf, h, q, 0) k = rfc6979(ec, hf, h, q) K = mult(ec, k, ec.G) if legendre_symbol(K[1], ec._p) != 1: k = ec.n - k e = ssa._e(ec, hf, K[0], Q, h) s = (k + e * q) % ec.n # valid signature sig = ssa.sign(ec, hf, h, q, k) self.assertEqual((K[0], s), sig) # valid signature must validate self.assertTrue(ssa._verify(ec, hf, h, Q, sig))
def sign(ec: Curve, hf: Callable[[Any], Any], mhd: bytes, d: int, k: Optional[int] = None) -> ECSS: """ ECSSA signing operation according to bip-schnorr This signature scheme supports 32-byte messages. Differently from ECDSA, the 32-byte message can be a digest of other messages, but it does not need to. https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki """ # the bitcoin proposed standard is only valid for curves # whose prime p = 3 % 4 if not ec.pIsThreeModFour: errmsg = 'curve prime p must be equal to 3 (mod 4)' raise ValueError(errmsg) # The message mhd: a 32-byte array _ensure_msg_size(hf, mhd) # The secret key d: an integer in the range 1..n-1. if not 0 < d < ec.n: raise ValueError(f"private key {hex(d)} not in [1, n-1]") P = mult(ec, d, ec.G) # Fail if k' = 0. if k is None: k = rfc6979(ec, hf, mhd, d) if not 0 < k < ec.n: raise ValueError(f"ephemeral key {hex(k)} not in [1, n-1]") # Let R = k'G. RJ = _mult_jac(ec, k, ec.GJ) # break the simmetry: any criteria might have been used, # jacobi is the proposed bitcoin standard # Let k = k' if jacobi(y(R)) = 1, otherwise let k = n - k'. if legendre_symbol(RJ[1] * RJ[2] % ec._p, ec._p) != 1: k = ec.n - k Z2 = RJ[2] * RJ[2] r = (RJ[0] * mod_inv(Z2, ec._p)) % ec._p # Let e = int(hf(bytes(x(R)) || bytes(dG) || mhd)) mod n. e = _e(ec, hf, r, P, mhd) s = (k + e * d) % ec.n # s=0 is ok: in verification there is no inverse of s # The signature is bytes(x(R) || bytes((k + ed) mod n)). return r, s
def test_low_cardinality(self): """test all msg/key pairs of low cardinality elliptic curves""" # ec.n has to be prime to sign prime = [11, 13, 17, 19] for ec in low_card_curves: # only low card or it would take forever if ec._p in prime: # only few curves or it would take too long for d in range(1, ec.n): # all possible private keys P = mult(ec, d, ec.G) # public key for e in range(ec.n): # all possible int from hash for k in range(1, ec.n): # all possible ephemeral keys R = mult(ec, k, ec.G) r = R[0] % ec.n if r == 0: self.assertRaises(ValueError, dsa._sign, ec, e, d, k) continue s = mod_inv(k, ec.n) * (e + d * r) % ec.n if s == 0: self.assertRaises(ValueError, dsa._sign, ec, e, d, k) continue # bitcoin canonical 'low-s' encoding for ECDSA if s > ec.n / 2: s = ec.n - s # valid signature sig = dsa._sign(ec, e, d, k) self.assertEqual((r, s), sig) # valid signature must validate self.assertTrue(dsa._verhlp(ec, e, P, sig)) keys = dsa._pubkey_recovery(ec, e, sig) self.assertIn(P, keys) for Q in keys: self.assertTrue(dsa._verhlp(ec, e, Q, sig))
def sign(msg: bytes, k: List[int], sign_key_idx: List[int], sign_keys: List[int], pubk_rings: Dict[int, List[Point]]) -> Tuple[bytes, Dict[int, List[int]]]: """ Borromean ring signature - signing algorithm https://github.com/ElementsProject/borromean-signatures-writeup https://github.com/Blockstream/borromean_paper/blob/master/borromean_draft_0.01_9ade1e49.pdf inputs: - msg: msg to be signed (bytes) - sign_key_idx: list of indexes representing each signing key per ring - sign_keys: list containing the whole set of signing keys (one per ring) - pubk_rings: dictionary of lists where internal lists represent single rings of pubkeys """ s: Dict[int, List[int]] = defaultdict(list) e: Dict[int, List[int]] = defaultdict(list) m = _get_msg_format(msg, pubk_rings) e0bytes = m ring_size = len(pubk_rings) # step 1 for i in range(ring_size): keys_size = len(pubk_rings[i]) s[i] = [0]*keys_size e[i] = [0]*keys_size j_star = sign_key_idx[i] start_idx = (j_star + 1) % keys_size R = octets_from_point(ec, mult(ec, k[i], ec.G), True) if start_idx != 0: for j in range(start_idx, keys_size): s[i][j] = random.getrandbits(256) e[i][j] = int_from_bits(ec, _hash(m, R, i, j)) assert 0 < e[i][j] < ec.n, "sign fail: how did you do that?!?" T = double_mult(ec, s[i][j], ec.G, -e[i][j], pubk_rings[i][j]) R = octets_from_point(ec, T, True) e0bytes += R e0 = hf(e0bytes).digest() # step 2 for i in range(ring_size): e[i][0] = int_from_bits(ec, _hash(m, e0, i, 0)) assert 0 < e[i][0] < ec.n, "sign fail: how did you do that?!?" j_star = sign_key_idx[i] for j in range(1, j_star+1): s[i][j-1] = random.getrandbits(256) T = double_mult(ec, s[i][j-1], ec.G, -e[i][j-1], pubk_rings[i][j-1]) R = octets_from_point(ec, T, True) e[i][j] = int_from_bits(ec, _hash(m, R, i, j)) assert 0 < e[i][j] < ec.n, "sign fail: how did you do that?!?" s[i][j_star] = k[i] + sign_keys[i]*e[i][j_star] return e0, s
def test_opposite(self): for ec in all_curves: Q = mult(ec, ec._p, ec.G) # just a random point, not Inf minus_Q = ec.opposite(Q) self.assertEqual(ec.add(Q, minus_Q), Inf) # jacobian coordinates Qjac = _jac_from_aff(Q) minus_Qjac = _jac_from_aff(minus_Q) self.assertEqual(ec._add_jac(Qjac, minus_Qjac)[2], 0) # opposite of Inf is Inf minus_Inf = ec.opposite(Inf) self.assertEqual(minus_Inf, Inf)
def test_is_on_curve() -> None: for ec in all_curves.values(): with pytest.raises(ValueError, match="point must be a tuple"): ec.is_on_curve("not a point") # type: ignore with pytest.raises(ValueError, match="x-coordinate not in 0..p-1: "): ec.y(ec.p) # just a random point, not INF q = 1 + secrets.randbelow(ec.n - 1) Q = mult(q, ec.G, ec) with pytest.raises(ValueError, match="y-coordinate not in 1..p-1: "): ec.is_on_curve((Q[0], ec.p))
def test_ecdh() -> None: ec = CURVES["secp256k1"] hf = sha256 a, A = dsa.gen_keys() # Alice b, B = dsa.gen_keys() # Bob # Alice computes the shared secret using Bob's public key shared_secret_a = mult(a, B) # Bob computes the shared secret using Alice's public key shared_secret_b = mult(b, A) assert shared_secret_a == shared_secret_b assert shared_secret_a == mult(a * b, ec.G) # hash the shared secret to remove weak bits shared_secret_field_element = shared_secret_a[0] z = shared_secret_field_element.to_bytes(ec.psize, "big") shared_info = b"deadbeef" hsize = hf().digest_size for size in (hsize - 1, hsize, hsize + 1): shared_key = ansi_x9_63_kdf(z, size, hf, None) assert len(shared_key) == size assert shared_key == diffie_hellman(a, B, size, None, ec, hf) assert shared_key == diffie_hellman(b, A, size, None, ec, hf) shared_key = ansi_x9_63_kdf(z, size, hf, shared_info) assert len(shared_key) == size assert shared_key == diffie_hellman(a, B, size, shared_info, ec, hf) assert shared_key == diffie_hellman(b, A, size, shared_info, ec, hf) max_size = hsize * (2 ** 32 - 1) size = max_size + 1 with pytest.raises(BTClibValueError, match="cannot derive a key larger than "): ansi_x9_63_kdf(z, size, hf, None)
def verify_commit(c: bytes, ec: Curve, hf, receipt: Receipt) -> bool: w, R = receipt # w in [1..n-1] dsa # w in [1..p-1] ssa # different verify functions? # verify R is a good point? ch = hf(c).digest() e = hf(octets_from_point(ec, R, True) + ch).digest() e = int_from_bits(ec, e) W = ec.add(R, mult(ec, e, ec.G)) # different verify functions? # return w == W[0] # ECSS return w == W[0] % ec.n # ECDS, FIXME: ECSSA
def test_signtocontract(self): prv = 0x1 pub = mult(ec, prv, ec.G) m = "to be signed".encode() c = "to be committed".encode() dsa_sig, dsa_receipt = ecdsa_commit_sign(c, ec, hf, m, prv, None) self.assertTrue(dsa.verify(ec, hf, m, pub, dsa_sig)) self.assertTrue(verify_commit(c, ec, hf, dsa_receipt)) # 32 bytes message for ECSSA m = hf(m).digest() ssa_sig, ssa_receipt = ecssa_commit_sign(c, ec, hf, m, prv, None) self.assertTrue(ssa.verify(ec, hf, m, pub, ssa_sig)) self.assertTrue(verify_commit(c, ec, hf, ssa_receipt))
def test_pubkey_recovery(self): ec = secp112r2 hf = sha256 q = 0x1 Q = mult(ec, q, ec.G) msg = 'Satoshi Nakamoto'.encode() sig = dsa.sign(ec, hf, msg, q) self.assertTrue(dsa.verify(ec, hf, msg, Q, sig)) self.assertTrue(dsa._verify(ec, hf, msg, Q, sig)) keys = dsa.pubkey_recovery(ec, hf, msg, sig) self.assertIn(Q, keys) for Q in keys: self.assertTrue(dsa.verify(ec, hf, msg, Q, sig)) self.assertTrue(dsa._verify(ec, hf, msg, Q, sig))
def test_boscoster(self): ec = secp256k1 k: List[int] = list() ksum = 0 for i in range(11): k.append(random.getrandbits(ec.nlen) % ec.n) ksum += k[i] P = [ec.G] * len(k) boscoster = multi_mult(ec, k, P) self.assertEqual(boscoster, mult(ec, ksum, ec.G)) # mismatch between scalar length and Points length P = [ec.G] * (len(k)-1) self.assertRaises(ValueError, multi_mult, ec, k, P)
def test_add_double_aff_jac() -> None: "Test consistency between affine and Jacobian add/double methods." for ec in all_curves.values(): # just a random point, not INF q = 1 + secrets.randbelow(ec.n - 1) Q = mult(q, ec.G, ec) QJ = _jac_from_aff(Q) # add Q and G R = ec._add_aff(Q, ec.G) RJ = ec._add_jac(QJ, ec.GJ) assert R == ec._aff_from_jac(RJ) # double Q R = ec._double_aff(Q) RJ = ec._double_jac(QJ) assert R == ec._aff_from_jac(RJ) assert R == ec._add_aff(Q, Q) assert ec._jac_equality(RJ, ec._add_jac(QJ, QJ))
def test_aff_jac_conversions() -> None: for ec in all_curves.values(): # just a random point, not INF q = 1 + secrets.randbelow(ec.n - 1) Q = mult(q, ec.G, ec) QJ = _jac_from_aff(Q) assert Q == ec._aff_from_jac(QJ) x_Q = ec._x_aff_from_jac(QJ) assert Q[0] == x_Q assert INF == ec._aff_from_jac(_jac_from_aff(INF)) # relevant for BIP340-Schnorr signature verification assert not ec.has_square_y(INF) with pytest.raises(ValueError, match="infinity point has no x-coordinate"): ec._x_aff_from_jac(INFJ) with pytest.raises(TypeError, match="not a point"): ec.has_square_y("notapoint") # type: ignore
def test_aff_jac_conversions() -> None: for ec in all_curves.values(): # just a random point, not INF q = 1 + secrets.randbelow(ec.n - 1) Q = mult(q, ec.G, ec) QJ = _jac_from_aff(Q) assert Q == ec._aff_from_jac(QJ) x_Q = ec._x_aff_from_jac(QJ) assert Q[0] == x_Q y_Q = ec._y_aff_from_jac(QJ) assert Q[1] == y_Q assert INF == ec._aff_from_jac(_jac_from_aff(INF)) with pytest.raises(BTClibValueError, match="INF has no x-coordinate"): ec._x_aff_from_jac(INFJ) with pytest.raises(BTClibValueError, match="INF has no y-coordinate"): ec._y_aff_from_jac(INFJ)
def test_borromean(self): ec = secp256k1 ring_number = 4 ring_dim = [random.randint(1, 4) for ring in range(ring_number)] borromean.signing_indexes = [random.randrange(ring_dim[ring]) for ring in range(ring_number)] priv_keys = {} Pub_keys = {} borromean.signing_keys = [] for i in range(ring_number): priv_keys[i] = [0]*ring_dim[i] Pub_keys[i] = [0]*ring_dim[i] for j in range(ring_dim[i]): priv_keys[i][j] = j+1 Pub_keys[i][j] = mult(ec, priv_keys[i][j], ec.G) borromean.signing_keys.append(priv_keys[i][borromean.signing_indexes[i]]) msg = 'Borromean ring borromean.signature'.encode() sig = borromean.sign(msg, list(range(1, 5)), borromean.signing_indexes, borromean.signing_keys, Pub_keys) self.assertTrue(borromean.verify(msg, sig[0], sig[1], Pub_keys)) self.assertFalse(borromean.verify(0, sig[0], sig[1], Pub_keys))
def test_pubkey_recovery() -> None: ec = CURVES["secp112r2"] q = 0x10 Q = mult(q, ec.G, ec) msg = "Satoshi Nakamoto" low_s = True sig = dsa.sign(msg, q, low_s, ec) assert dsa.verify(msg, Q, sig, ec) dersig = dsa.serialize(*sig, ec) assert dsa.verify(msg, Q, dersig, ec) r, s = dsa.deserialize(dersig) assert (r, s) == sig keys = dsa.recover_pubkeys(msg, sig, ec) assert len(keys) == 4 assert Q in keys for Q in keys: assert dsa.verify(msg, Q, sig, ec)
def test_shamir(self): ec = ec23_31 for k1 in range(ec.n): for k2 in range(ec.n): shamir = double_mult(ec, k1, ec.G, k2, ec.G) std = ec.add(mult(ec, k1, ec.G), mult(ec, k2, ec.G)) self.assertEqual(shamir, std) shamir = double_mult(ec, k1, Inf, k2, ec.G) std = ec.add(mult(ec, k1, Inf), mult(ec, k2, ec.G)) self.assertEqual(shamir, std) shamir = double_mult(ec, k1, ec.G, k2, Inf) std = ec.add(mult(ec, k1, ec.G), mult(ec, k2, Inf)) self.assertEqual(shamir, std)