def test_batch_validation(): ec = secp256k1 hsize = hf().digest_size hlen = hsize * 8 ms = [] Qs = [] sigs = [] ms.append(secrets.randbits(hlen).to_bytes(hsize, "big")) q = 1 + secrets.randbelow(ec.n - 1) # bytes version Qs.append(mult(q, ec.G, ec)[0].to_bytes(ec.psize, "big")) sigs.append(ssa.sign(ms[0], q, None, ec, hf)) # test with only 1 sig ssa._batch_verify(ms, Qs, sigs, ec, hf) for _ in range(3): mhd = secrets.randbits(hlen).to_bytes(hsize, "big") ms.append(mhd) q = 1 + secrets.randbelow(ec.n - 1) # Point version Qs.append(mult(q, ec.G, ec)) sigs.append(ssa.sign(mhd, q, None, ec, hf)) ssa._batch_verify(ms, Qs, sigs, ec, hf) assert ssa.batch_verify(ms, Qs, sigs, ec, hf) ms.append(ms[0]) sigs.append(sigs[1]) Qs.append(Qs[0]) assert not ssa.batch_verify(ms, Qs, sigs, ec, hf) err_msg = "signature verification precondition failed" with pytest.raises(ValueError, match=err_msg): ssa._batch_verify(ms, Qs, sigs, ec, hf) sigs[-1] = sigs[0] # valid again ms[-1] = ms[0][:-1] err_msg = "invalid size: 31 bytes instead of 32" with pytest.raises(ValueError, match=err_msg): ssa._batch_verify(ms, Qs, sigs, ec, hf) ms[-1] = ms[0] # valid again ms.append(ms[0]) # add extra message err_msg = "mismatch between number of pubkeys " with pytest.raises(ValueError, match=err_msg): ssa._batch_verify(ms, Qs, sigs, ec, hf) ms.pop() # valid again sigs.append(sigs[0]) # add extra sig err_msg = "mismatch between number of pubkeys " with pytest.raises(ValueError, match=err_msg): ssa._batch_verify(ms, Qs, sigs, ec, hf) sigs.pop() # valid again err_msg = "field prime is not equal to 3 mod 4: " with pytest.raises(ValueError, match=err_msg): ssa._batch_verify(ms, Qs, sigs, secp224k1, hf)
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(dU, ec.G, ec) self.assertEqual(QU, (466448783855397898016055842232266600516272889280, 1110706324081757720403272427311003102474457754220)) self.assertEqual( octets_from_point(QU, True, ec).hex(), '0251b4496fecc406ed0e75a24a3c03206251419dc0') # 4.1.3 dV = 399525573676508631577122671218044116107572676710 self.assertEqual(format(dV, str(ec.psize) + 'x'), '45fb58a92a17ad4b15101c66e74f277e2b460866') QV = mult(dV, ec.G, ec) self.assertEqual(QV, (420773078745784176406965940076771545932416607676, 221937774842090227911893783570676792435918278531)) self.assertEqual( octets_from_point(QV, True, ec).hex(), '0349b41e0e9c0369c2328739d90f63d56707c6e5bc') # expected results z_exp = 1155982782519895915997745984453282631351432623114 zstr = 'ca7c0f8c3ffa87a96e1b74ac8e6af594347bb40a' size = 20 keying_data_exp = '744ab703f5bc082e59185f6d049d2d367db245c2' # 4.1.4 z, _ = mult(dU, QV, ec) # x coordinate only self.assertEqual(z, z_exp) self.assertEqual(format(z, str(ec.psize) + 'x'), zstr) keyingdata = dh.ansi_x963_kdf(octets_from_int(z, ec.psize), size, ec, hf) self.assertEqual(keyingdata.hex(), keying_data_exp) # 4.1.5 z, _ = mult(dV, QU, ec) # x coordinate only self.assertEqual(z, z_exp) self.assertEqual(format(z, str(ec.psize) + 'x'), zstr) keyingdata = dh.ansi_x963_kdf(octets_from_int(z, ec.psize), size, ec, hf) self.assertEqual(keyingdata.hex(), keying_data_exp)
def test_signature(self): q = 0x1 Q = mult(q) msg = b'Satoshi Nakamoto' sig = dsa.sign(msg, q) # https://bitcointalk.org/index.php?topic=285142.40 # Deterministic Usage of DSA and ECDSA (RFC 6979) exp_sig = ( 0x934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d8, 0x2442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5) r, s = sig self.assertEqual(sig[0], exp_sig[0]) self.assertIn(sig[1], (exp_sig[1], secp256k1.n - exp_sig[1])) self.assertTrue(dsa.verify(msg, Q, sig)) self.assertTrue(dsa._verify(msg, Q, sig)) # malleability malleated_sig = (r, secp256k1.n - s) self.assertTrue(dsa.verify(msg, Q, malleated_sig)) self.assertTrue(dsa._verify(msg, Q, malleated_sig)) keys = dsa.pubkey_recovery(msg, sig) self.assertTrue(len(keys) == 2) self.assertIn(Q, keys) fmsg = b'Craig Wright' self.assertFalse(dsa.verify(fmsg, Q, sig)) self.assertFalse(dsa._verify(fmsg, Q, sig)) fdsasig = (sig[0], sig[1], sig[1]) self.assertFalse(dsa.verify(msg, Q, fdsasig)) self.assertRaises(TypeError, dsa._verify, msg, Q, fdsasig) fq = 0x4 fQ = mult(fq) self.assertFalse(dsa.verify(msg, fQ, sig)) self.assertFalse(dsa._verify(msg, fQ, sig)) # r not in [1, n-1] invalid_dassig = 0, sig[1] self.assertFalse(dsa.verify(msg, Q, invalid_dassig)) # s not in [1, n-1] invalid_dassig = sig[0], 0 self.assertFalse(dsa.verify(msg, Q, invalid_dassig)) # pubkey = Inf self.assertRaises(ValueError, dsa._verify, msg, (1, 0), sig) #dsa._verify(msg, (1, 0), sig) # private key not in [1, n-1] self.assertRaises(ValueError, dsa.sign, msg, 0) #dsa.sign(msg, 0) # ephemeral key not in [1, n-1] self.assertRaises(ValueError, dsa.sign, msg, 1, 0)
def test_key_deployment(): """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 assert format(dU, str(ec.nsize) + "x") == "aa374ffc3ce144e6b073307972cb6d57b2a4e982" QU = mult(dU, ec.G, ec) assert QU == ( 466448783855397898016055842232266600516272889280, 1110706324081757720403272427311003102474457754220, ) assert (bytes_from_point( QU, ec).hex() == "0251b4496fecc406ed0e75a24a3c03206251419dc0") # 4.1.3 dV = 399525573676508631577122671218044116107572676710 assert format(dV, str(ec.nsize) + "x") == "45fb58a92a17ad4b15101c66e74f277e2b460866" QV = mult(dV, ec.G, ec) assert QV == ( 420773078745784176406965940076771545932416607676, 221937774842090227911893783570676792435918278531, ) assert (bytes_from_point( QV, ec).hex() == "0349b41e0e9c0369c2328739d90f63d56707c6e5bc") # expected results z_exp = 1155982782519895915997745984453282631351432623114 zstr = "ca7c0f8c3ffa87a96e1b74ac8e6af594347bb40a" size = 20 keying_data_exp = "744ab703f5bc082e59185f6d049d2d367db245c2" # 4.1.4 z, _ = mult(dU, QV, ec) # x coordinate only assert z == z_exp assert format(z, str(ec.psize) + "x") == zstr keyingdata = dh.ansi_x963_kdf(z.to_bytes(ec.psize, "big"), size, ec, hf) assert keyingdata.hex() == keying_data_exp # 4.1.5 z, _ = mult(dV, QU, ec) # x coordinate only assert z == z_exp assert format(z, str(ec.psize) + "x") == zstr keyingdata = dh.ansi_x963_kdf(z.to_bytes(ec.psize, "big"), size, ec, hf) assert keyingdata.hex() == keying_data_exp
def test_batch_validation(self): hsize = hf().digest_size hlen = hsize * 8 ms = [] Qs = [] sigs = [] ms.append(secrets.randbits(hlen).to_bytes(hsize, 'big')) q = 1 + secrets.randbelow(ec.n - 1) # bytes version Qs.append(mult(q, ec.G, ec)[0].to_bytes(ec.psize, 'big')) sigs.append(ssa.sign(ms[0], q, None, ec, hf)) # test with only 1 sig ssa._batch_verify(ms, Qs, sigs, ec, hf) for _ in range(3): mhd = secrets.randbits(hlen).to_bytes(hsize, 'big') ms.append(mhd) q = 1 + secrets.randbelow(ec.n - 1) # Point version Qs.append(mult(q, ec.G, ec)) sigs.append(ssa.sign(mhd, q, None, ec, hf)) ssa._batch_verify(ms, Qs, sigs, ec, hf) self.assertTrue(ssa.batch_verify(ms, Qs, sigs, ec, hf)) # invalid sig ms.append(ms[0]) sigs.append(sigs[1]) Qs.append(Qs[0]) self.assertFalse(ssa.batch_verify(ms, Qs, sigs, ec, hf)) self.assertRaises(AssertionError, ssa._batch_verify, ms, Qs, sigs, ec, hf) # ssa._batch_verify(ms, Qs, sigs, ec, hf) sigs[-1] = sigs[0] # valid again # Invalid size: 31 bytes instead of 32 ms[-1] = ms[0][:-1] self.assertRaises(ValueError, ssa._batch_verify, ms, Qs, sigs, ec, hf) # ssa._batch_verify(ms, Qs, sigs, ec, hf) ms[-1] = ms[0] # valid again # mismatch between number of pubkeys (5) and number of messages (6) ms.append(ms[0]) # add extra message self.assertRaises(ValueError, ssa._batch_verify, ms, Qs, sigs, ec, hf) # ssa._batch_verify(ms, Qs, sigs, ec, hf) ms.pop() # valid again # mismatch between number of pubkeys (5) and number of signatures (6) sigs.append(sigs[0]) # add extra sig self.assertRaises(ValueError, ssa._batch_verify, ms, Qs, sigs, ec, hf) # ssa._batch_verify(ms, Qs, sigs, ec, hf) sigs.pop() # valid again # field prime p is not equal to 3 (mod 4) self.assertRaises(ValueError, ssa._batch_verify, ms, Qs, sigs, secp224k1, hf)
def test_ecdh(self): size = 20 dU = 0x1 QU = mult(dU, ec.G, ec) dV = 0x2 QV = mult(dV, ec.G, ec) keyingdataU = dh.diffie_hellman(dh.ansi_x963_kdf, dU, QV, size, ec, hf) keyingdataV = dh.diffie_hellman(dh.ansi_x963_kdf, dV, QU, size, ec, hf) self.assertEqual(keyingdataU, keyingdataV)
def test_ecssa(self): """Basic tests""" q = 0x1 Q = mult(q) msg = sha256(b'Satoshi Nakamoto').digest() sig = ssa.sign(msg, q, None) # no source for the following... but # https://bitcointalk.org/index.php?topic=285142.40 # same r because of rfc6979 exp_sig = ( 0x934B1EA10A4B3C1757E2B0C017D0B6143CE3C9A7E6A4A49860D7A6AB210EE3D8, 0x2DF2423F70563E3C4BD0E00BDEF658081613858F110ECF937A2ED9190BF4A01A) self.assertEqual(sig[0], exp_sig[0]) self.assertEqual(sig[1], exp_sig[1]) ssa._verify(msg, Q, sig) self.assertTrue(ssa.verify(msg, Q, sig)) self.assertTrue(ssa._verify(msg, Q, sig)) fmsg = sha256(b'Craig Wright').digest() self.assertFalse(ssa.verify(fmsg, Q, sig)) self.assertFalse(ssa._verify(fmsg, Q, sig)) fssasig = (sig[0], sig[1], sig[1]) self.assertFalse(ssa.verify(msg, Q, fssasig)) self.assertRaises(TypeError, ssa._verify, msg, Q, fssasig) # y(sG - eP) is not a quadratic residue fq = 0x2 fQ = mult(fq) self.assertFalse(ssa.verify(msg, fQ, sig)) self.assertRaises(ValueError, ssa._verify, msg, fQ, sig) fq = 0x4 fQ = mult(fq) self.assertFalse(ssa.verify(msg, fQ, sig)) self.assertFalse(ssa._verify(msg, fQ, sig)) # not ec.pIsThreeModFour self.assertFalse(ssa.verify(msg, Q, sig, secp224k1)) self.assertRaises(ValueError, ssa._verify, msg, Q, sig, secp224k1) # verify: message of wrong size wrongmsg = msg[:-1] self.assertFalse(ssa.verify(wrongmsg, Q, sig)) self.assertRaises(ValueError, ssa._verify, wrongmsg, Q, sig) #ssa._verify(wrongmsg, Q, sig) # sign: message of wrong size self.assertRaises(ValueError, ssa.sign, wrongmsg, q, None) #ssa.sign(wrongmsg, q, None) # invalid (zero) challenge e self.assertRaises(ValueError, ssa._pubkey_recovery, 0, sig)
def test_ecdh(): size = 20 dU = 0x1 QU = mult(dU, ec.G, ec) dV = 0x2 QV = mult(dV, ec.G, ec) keyingdataU = dh.diffie_hellman(dh.ansi_x963_kdf, dU, QV, size, ec, hf) keyingdataV = dh.diffie_hellman(dh.ansi_x963_kdf, dV, QU, size, ec, hf) assert keyingdataU == keyingdataV
def test_batch_validation(self): ec = secp256k1 hf = sha256 m = [] sig = [] Q = [] hsize = hf().digest_size hlen = hsize * 8 m.append(random.getrandbits(hlen).to_bytes(hsize, byteorder='big')) q = (1 + random.getrandbits(ec.nlen)) % ec.n sig.append(ssa.sign(m[0], q, None, ec, hf)) Q.append(mult(q, ec.G, ec)) # test with only 1 sig self.assertTrue(ssa.batch_verify(m, Q, sig, ec, hf)) for i in range(1, 4): m.append(random.getrandbits(hlen).to_bytes(hsize, byteorder='big')) q = (1 + random.getrandbits(ec.nlen)) % ec.n sig.append(ssa.sign(m[i], q, None, ec, hf)) Q.append(mult(q, ec.G, ec)) self.assertTrue(ssa.batch_verify(m, Q, sig, ec, hf)) # invalid sig m.append(m[0]) sig.append(sig[1]) Q.append(Q[0]) self.assertFalse(ssa.batch_verify(m, Q, sig, ec, hf)) #ssa._batch_verify(m, Q, sig, ec, hf) sig[-1] = sig[0] # valid again # invalid 31 bytes message m[-1] = m[0][:-1] self.assertFalse(ssa.batch_verify(m, Q, sig, ec, hf)) #ssa._batch_verify(m, Q, sig, ec, hf) m[-1] = m[0] # valid again # mismatch between number of pubkeys and number of messages m.append(m[0]) # add extra message self.assertRaises(ValueError, ssa._batch_verify, m, Q, sig, ec, hf) #ssa._batch_verify(m, Q, sig, ec, hf) m.pop() # valid again # mismatch between number of pubkeys and number of signatures sig.append(sig[0]) # add extra sig self.assertRaises(ValueError, ssa._batch_verify, m, Q, sig, ec, hf) #ssa._batch_verify(m, Q, sig, ec, hf) sig.pop() # valid again # curve prime p must be equal to 3 (mod 4) ec = secp224k1 self.assertRaises(ValueError, ssa._batch_verify, m, Q, sig, ec, hf)
def test_crack_prvkey(): ec = secp256k1 q = 0x19E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725 x_Q = mult(q)[0] msg1 = "Paolo is afraid of ephemeral random numbers" msg1 = hf(msg1.encode()).digest() k = ssa.k(msg1, q) sig1 = ssa.sign(msg1, q, k) msg2 = "and Paolo is right to be afraid" msg2 = hf(msg2.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_add(self): for ec in all_curves.values(): Q1 = mult(ec.p, ec.G, ec) # 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)) # negate points Q1opp = ec.negate(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_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(dU, ec.G, ec) self.assertEqual(QU, (466448783855397898016055842232266600516272889280, 1110706324081757720403272427311003102474457754220)) self.assertEqual( bytes_from_point(QU, True, ec).hex(), '0251b4496fecc406ed0e75a24a3c03206251419dc0') # 2.1.3 Signing Operation for U msg = b'abc' k = 702232148019446860144825009548118511996283736794 exp_sig = (0xCE2873E5BE449563391FEB47DDCBA2DC16379191, 0x3480EC1371A091A464B31CE47DF0CB8AA2D98B54) sig = dsa.sign(msg, dU, k, ec, hf) 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(msg, QU, sig, ec, hf))
def test_rfc6979_tv(): 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, ec, eval("hashlib." + hf)) assert r == hex(sig[0]) assert s in (hex(sig[1]), hex(ec.n - sig[1])) # test that RFC6979 is the default nonce for DSA sig = dsa.sign(msg, x, k=None, ec=ec, hf=eval("hashlib." + hf)) assert r == hex(sig[0]) assert s in (hex(sig[1]), hex(ec.n - 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_crack_prvkey(self): q = 0x6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725DEADBEEF x_Q = mult(q)[0] k = 1010101010101010101 msg1 = "Paolo is afraid of ephemeral random numbers" msg1 = hf(msg1.encode()).digest() sig1 = ssa.sign(msg1, q, k) # print(f'\nmsg1: {msg1.hex().upper()}') # print(f' r1: {hex(sig1[0]).upper()}') # print(f' s1: {hex(sig1[1]).upper()}') msg2 = "and Paolo is right to be afraid" msg2 = hf(msg2.encode()).digest() sig2 = ssa.sign(msg2, q, k) # print(f'\nmsg2: {msg2.hex().upper()}') # print(f' r2: {hex(sig2[0]).upper()}') # print(f' s2: {hex(sig2[1]).upper()}') qc, kc = ssa.crack_prvkey(msg1, sig1, msg2, sig2, x_Q) self.assertIn(q, (qc, ec.n - qc)) self.assertIn(k, (kc, ec.n - kc)) self.assertRaises(ValueError, ssa.crack_prvkey, msg1, sig1, msg2, (16, sig1[1]), x_Q) self.assertRaises(ValueError, ssa.crack_prvkey, msg1, sig1, msg1, sig1, x_Q)
def test_all_curves(self): for ec in all_curves.values(): self.assertEqual(mult(0, ec.G, ec), INF) self.assertEqual(mult(0, ec.G, ec), INF) self.assertEqual(mult(1, ec.G, ec), ec.G) self.assertEqual(mult(1, ec.G, ec), 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 = bytes_from_point(ec.G, ec) G2 = point_from_octets(Gbytes, ec) self.assertEqual(ec.G, G2) Gbytes = bytes_from_point(ec.G, ec, False) G2 = point_from_octets(Gbytes, ec) 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(2, ec.G, ec)) P = mult(ec.n - 1, ec.G, ec) self.assertEqual(ec.add(P, ec.G), INF) self.assertEqual(mult(ec.n, ec.G, ec), INF) self.assertEqual(mult(0, INF, ec), INF) self.assertEqual(mult(1, INF, ec), INF) self.assertEqual(mult(25, INF, ec), INF) ec_repr = repr(ec) if ec in low_card_curves.values() 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(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 test_ecssa(self): """Basic tests""" q = 0x1 Q = mult(q) mhd = hf(b'Satoshi Nakamoto').digest() sig = ssa.sign(mhd, q, None) ssa._verify(mhd, Q, sig, ec, hf) self.assertTrue(ssa.verify(mhd, Q, sig)) fmhd = hf(b'Craig Wright').digest() self.assertRaises(AssertionError, ssa._verify, fmhd, Q, sig, ec, hf) fssasig = (sig[0], sig[1], sig[1]) self.assertRaises(ValueError, ssa._verify, mhd, Q, fssasig, ec, hf) # y(sG - eP) is not a quadratic residue fq = 0x2 fQ = mult(fq) self.assertRaises(ValueError, ssa._verify, mhd, fQ, sig, ec, hf) fq = 0x4 fQ = mult(fq) self.assertRaises(AssertionError, ssa._verify, mhd, fQ, sig, ec, hf) # not ec.pIsThreeModFour self.assertRaises(ValueError, ssa._verify, mhd, Q, sig, secp224k1, hf) # verify: message of wrong size wrongmhd = mhd[:-1] self.assertRaises(ValueError, ssa._verify, wrongmhd, Q, sig, ec, hf) #ssa._verify(wrongmhd, Q, sig) # sign: message of wrong size self.assertRaises(ValueError, ssa.sign, wrongmhd, q, None) #ssa.sign(wrongmhd, q, None) # invalid (zero) challenge e self.assertRaises(ValueError, ssa._recover_pubkeys, 0, sig[0], sig[1], ec) #ssa._recover_pubkeys(0, sig) # not a BIP340 public key self.assertRaises(ValueError, ssa.to_bip340_pubkey_tuple, ["not", "a BIP340", "public key"])
def test_low_cardinality(self): """test low-cardinality curves for all msg/key pairs.""" # ec.n has to be prime to sign prime = [11, 13, 17, 19] # for dsa it is possible to directy iterate on all # possible values for e; # for ssa we have to iterate over all possible hash values hsize = hf().digest_size H = [i.to_bytes(hsize, 'big') for i in range(max(prime) * 4)] # 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 # BIP340 Schnorr only applies to curve whose prime p = 3 %4 if not ec.pIsThreeModFour: self.assertRaises(ValueError, ssa.sign, H[0], 1, None, ec) continue for q in range(1, ec.n): # all possible private keys Q = mult(q, ec.G, ec) # public key if not ec.has_square_y(Q): q = ec.n - q for h in H: # all possible hashed messages k = ssa.k(h, q, ec, hf) K = mult(k, ec.G, ec) if not ec.has_square_y(K): k = ec.n - k x_K = K[0] try: c = ssa._challenge(x_K, Q[0], h, ec, hf) except Exception: pass else: s = (k + c * q) % ec.n sig = ssa.sign(h, q, None, ec) self.assertEqual((x_K, s), sig) # valid signature must validate self.assertIsNone(ssa._verify(h, Q, sig, ec, hf)) if c != 0: # FIXME x_Q = ssa._recover_pubkeys(c, x_K, s, ec) self.assertEqual(Q[0], x_Q)
def test_octets2point(self): for ec in all_curves.values(): Q = mult(ec.p, ec.G, ec) # just a random point, not INF Q_bytes = b'\x03' if Q[1] & 1 else b'\x02' Q_bytes += Q[0].to_bytes(ec.psize, byteorder='big') R = point_from_octets(Q_bytes, ec) self.assertEqual(R, Q) self.assertEqual(bytes_from_point(R, ec), Q_bytes) Q_hex_str = Q_bytes.hex() R = point_from_octets(Q_hex_str, ec) self.assertEqual(R, Q) Q_bytes = b'\x04' + Q[0].to_bytes(ec.psize, byteorder='big') Q_bytes += Q[1].to_bytes(ec.psize, byteorder='big') R = point_from_octets(Q_bytes, ec) self.assertEqual(R, Q) self.assertEqual(bytes_from_point(R, ec, False), Q_bytes) Q_hex_str = Q_bytes.hex() R = point_from_octets(Q_hex_str, ec) self.assertEqual(R, Q) # scalar in point multiplication can be int, str, or bytes t = tuple() self.assertRaises(TypeError, mult, t, ec.G, ec) # not a compressed point Q_bytes = b'\x01' * (ec.psize + 1) self.assertRaises(ValueError, point_from_octets, Q_bytes, ec) # not a point Q_bytes += b'\x01' self.assertRaises(ValueError, point_from_octets, Q_bytes, ec) # not an uncompressed point Q_bytes = b'\x01' * 2 * (ec.psize + 1) self.assertRaises(ValueError, point_from_octets, Q_bytes, ec) # invalid x coordinate ec = CURVES['secp256k1'] x = 0xEEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34 xstr = format(x, '32X') self.assertRaises(ValueError, point_from_octets, "03" + xstr, ec) self.assertRaises(ValueError, point_from_octets, "04" + 2 * xstr, ec) self.assertRaises(ValueError, bytes_from_point, (x, x), ec) self.assertRaises(ValueError, bytes_from_point, (x, x), ec, False) # Point must be a tuple[int, int] P = x, x, x self.assertRaises(ValueError, ec.is_on_curve, P) # y-coordinate not in (0, p) P = x, ec.p + 1 self.assertRaises(ValueError, ec.is_on_curve, P)
def test_assorted_mult2(): ec = ec23_31 H = second_generator(ec) for k1 in range(-ec.n + 1, ec.n): K1 = mult(k1, ec.G, ec) for k2 in range(ec.n): K2 = mult(k2, H, ec) shamir = double_mult(k1, ec.G, k2, ec.G, ec) assert shamir == mult(k1 + k2, ec.G, ec) shamir = double_mult(k1, INF, k2, H, ec) assert shamir == K2 shamir = double_mult(k1, ec.G, k2, INF, ec) assert shamir == K1 shamir = double_mult(k1, ec.G, k2, H, ec) K1K2 = ec.add(K1, K2) assert K1K2 == shamir k3 = 1 + secrets.randbelow(ec.n - 1) K3 = mult(k3, ec.G, ec) K1K2K3 = ec.add(K1K2, K3) boscoster = multi_mult([k1, k2, k3], [ec.G, H, ec.G], ec) assert K1K2K3 == boscoster k4 = 1 + secrets.randbelow(ec.n - 1) K4 = mult(k4, H, ec) K1K2K3K4 = ec.add(K1K2K3, K4) points = [ec.G, H, ec.G, H] boscoster = multi_mult([k1, k2, k3, k4], points, ec) assert K1K2K3K4 == boscoster assert K1K2K3 == multi_mult([k1, k2, k3, 0], points, ec) assert K1K2 == multi_mult([k1, k2, 0, 0], points, ec) assert K1 == multi_mult([k1, 0, 0, 0], points, ec) assert INF == multi_mult([0, 0, 0, 0], points, ec) err_msg = "mismatch between number of scalars and points: " with pytest.raises(ValueError, match=err_msg): multi_mult([k1, k2, k3, k4], [ec.G, H, ec.G], ec)
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(d, ec.G, ec) # 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(k, ec.G, ec) r = R[0] % ec.n if r == 0: self.assertRaises(ValueError, dsa._sign, e, d, k, ec) continue s = mod_inv(k, ec.n) * (e + d * r) % ec.n if s == 0: self.assertRaises(ValueError, dsa._sign, e, d, k, ec) continue # bitcoin canonical 'low-s' encoding for ECDSA if s > ec.n / 2: s = ec.n - s # valid signature sig = dsa._sign(e, d, k, ec) self.assertEqual((r, s), sig) # valid signature must validate self.assertTrue(dsa._verhlp(e, P, sig, ec)) keys = dsa._pubkey_recovery(e, sig, ec) self.assertIn(P, keys) for Q in keys: self.assertTrue(dsa._verhlp(e, Q, sig, ec))
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, byteorder='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, H[0], 1, None, ec) continue for q in range(ec.n): # all possible private keys if q == 0: # invalid prvkey=0 self.assertRaises(ValueError, ssa.sign, H[0], q, None, ec) self.assertRaises(ValueError, rfc6979, H[0], q, ec) continue Q = mult(q, ec.G, ec) # public key for h in H: # all possible hashed messages # k = 0 self.assertRaises(ValueError, ssa.sign, h, q, 0, ec) k = rfc6979(h, q, ec) K = mult(k, ec.G, ec) if legendre_symbol(K[1], ec._p) != 1: k = ec.n - k e = ssa._e(K[0], Q, h, ec) s = (k + e * q) % ec.n # valid signature sig = ssa.sign(h, q, k, ec) self.assertEqual((K[0], s), sig) # valid signature must validate self.assertTrue(ssa._verify(h, Q, sig, ec))
def test_opposite(self): for ec in all_curves.values(): Q = mult(ec.p, ec.G, ec) # just a random point, not INF minus_Q = ec.negate(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) # negate of INF is INF minus_Inf = ec.negate(INF) self.assertEqual(minus_Inf, INF)
def test_signtocontract(self): prv = 0x1 pub = mult(prv) m = b"to be signed" c = b"to be committed" dsa_sig, dsa_receipt = ecdsa_commit_sign(c, m, prv, None) self.assertIsNone(dsa._verify(m, pub, dsa_sig, ec, sha256)) self.assertTrue(verify_commit(c, dsa_receipt)) # 32 bytes message for ECSSA m = sha256(m).digest() ssa_sig, ssa_receipt = ecssa_commit_sign(c, m, prv, None) self.assertIsNone(ssa._verify(m, pub, ssa_sig, ec, sha256)) self.assertTrue(verify_commit(c, ssa_receipt))
def test_aff_jac_conversions(self): for ec in all_curves.values(): Q = mult(ec.p, ec.G, ec) # just a random point, not INF QJ = _jac_from_aff(Q) checkQ = ec._aff_from_jac(QJ) self.assertEqual(Q, checkQ) x = ec._x_aff_from_jac(QJ) self.assertEqual(Q[0], x) checkInf = ec._aff_from_jac(_jac_from_aff(INF)) self.assertEqual(INF, checkInf) # relevant for BIP340-Schnorr signature verification self.assertFalse(ec.has_square_y(INF)) self.assertRaises(ValueError, ec._x_aff_from_jac, INFJ) self.assertRaises(ValueError, ec.has_square_y, "Not a Point")
def test_boscoster(self): ec = secp256k1 k: List[int] = list() ksum = 0 for i in range(11): k.append(secrets.randbits(ec.nlen) % ec.n) ksum += k[i] P = [ec.G] * len(k) boscoster = multi_mult(k, P, ec) self.assertEqual(boscoster, mult(ksum, ec.G, ec)) # mismatch between scalar length and Points length P = [ec.G] * (len(k) - 1) self.assertRaises(ValueError, multi_mult, k, P, ec)
def test_pubkey_recovery(self): ec = secp112r2 q = 0x10 Q = mult(q, ec.G, ec) msg = b'Satoshi Nakamoto' sig = dsa.sign(msg, q, None, ec) self.assertTrue(dsa.verify(msg, Q, sig, ec)) self.assertTrue(dsa._verify(msg, Q, sig, ec)) keys = dsa.pubkey_recovery(msg, sig, ec) self.assertEqual(len(keys), 4) self.assertIn(Q, keys) for Q in keys: self.assertTrue(dsa.verify(msg, Q, sig, ec)) self.assertTrue(dsa._verify(msg, Q, sig, ec))
def test_invalid_schnorr(): sighash = bytes.fromhex("00" * 32) sig = ssa.serialize(*ssa._sign(sighash, 1)) pubkey = bytes_from_point(mult(1)) pubkey_hash = hash256(pubkey) script = Script([ [0x00, 0x00, pubkey], [0x01, 0x00, sig], [0x02, 0x00, pubkey_hash], # push pubkey_hash [0x03, 0x02, b"\x00"], # hash of pub key from unlocking script [0xFF, 0x01, b"\x03\x02"], # check equality [0xFF, 0x04, b"\xff"], # exit if not equal [0xFF, 0x03, b"\x00\x01"], # schnorr verify [0xFF, 0x04, b"\xff"], ] # exit if not equal]) # push signature ) assert script.execute(memory={0x100: sighash})
def test_pubkey_recovery(self): ec = secp112r2 q = 0x10 Q = mult(q, ec.G, ec) msg = 'Satoshi Nakamoto' k = sighash = None sig = dsa.sign(msg, q, k, ec) self.assertTrue(dsa.verify(msg, Q, sig, ec)) dersig = der.serialize(*sig, sighash, ec) self.assertTrue(dsa.verify(msg, Q, dersig, ec)) r, s, _ = der.deserialize(dersig) self.assertEqual((r, s), sig) keys = dsa.recover_pubkeys(msg, dersig, ec) self.assertEqual(len(keys), 4) self.assertIn(Q, keys) for Q in keys: self.assertTrue(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(k1, ec.G, k2, ec.G, ec) std = ec.add(mult(k1, ec.G, ec), mult(k2, ec.G, ec)) self.assertEqual(shamir, std) shamir = double_mult(k1, INF, k2, ec.G, ec) std = ec.add(mult(k1, INF, ec), mult(k2, ec.G, ec)) self.assertEqual(shamir, std) shamir = double_mult(k1, ec.G, k2, INF, ec) std = ec.add(mult(k1, ec.G, ec), mult(k2, INF, ec)) self.assertEqual(shamir, std)