def test_signtocontract(): m = b"to be signed" c = b"to be committed" prv, pub = dsa.gen_keys() dsa_sig, dsa_receipt = ecdsa_commit_sign(c, m, prv, None) dsa.assert_as_valid(m, pub, dsa_sig, ec, sha256) assert verify_commit(c, dsa_receipt) # 32 bytes message for ECSSA m = sha256(m).digest() prv, pub = ssa.gen_keys() ssa_sig, ssa_receipt = ecssa_commit_sign(c, m, prv, None) ssa.assert_as_valid(m, pub, ssa_sig, ec, sha256) assert verify_commit(c, ssa_receipt)
def test_signature(): """Basic tests""" ec = secp256k1 q, x_Q = ssa.gen_keys(0x1) mhd = hf(b"Satoshi Nakamoto").digest() sig = ssa.sign(mhd, q, None) ssa.assert_as_valid(mhd, x_Q, sig, ec, hf) assert ssa.verify(mhd, x_Q, sig) assert sig == ssa.deserialize(sig) fmhd = hf(b"Craig Wright").digest() assert not ssa.verify(fmhd, x_Q, sig, ec, hf) err_msg = "signature verification failed" with pytest.raises(AssertionError, match=err_msg): ssa.assert_as_valid(fmhd, x_Q, sig, ec, hf) _, x_fQ = ssa.gen_keys(0x2) assert not ssa.verify(mhd, x_fQ, sig, ec, hf) err_msg = "y_K is not a quadratic residue" with pytest.raises(RuntimeError, match=err_msg): ssa.assert_as_valid(mhd, x_fQ, sig, ec, hf) _, x_fQ = ssa.gen_keys(0x4) assert not ssa.verify(mhd, x_fQ, sig, ec, hf) err_msg = "signature verification failed" with pytest.raises(AssertionError, match=err_msg): ssa.assert_as_valid(mhd, x_fQ, sig, ec, hf) err_msg = "not a BIP340 public key" with pytest.raises(ValueError, match=err_msg): ssa.assert_as_valid(mhd, INF, sig, ec, hf) assert not ssa.verify(mhd, x_Q, sig, secp224k1, hf) err_msg = "field prime is not equal to 3 mod 4: " with pytest.raises(ValueError, match=err_msg): ssa.assert_as_valid(mhd, x_Q, sig, secp224k1, hf) wrongmhd = mhd[:-1] assert not ssa.verify(wrongmhd, x_Q, sig, ec, hf) err_msg = "invalid size: 31 bytes instead of 32" with pytest.raises(ValueError, match=err_msg): ssa.assert_as_valid(wrongmhd, x_Q, sig, ec, hf) fssasig = (sig[0], sig[1], sig[1]) assert not ssa.verify(mhd, x_fQ, fssasig, ec, hf) err_msg = "too many values to unpack " with pytest.raises(ValueError, match=err_msg): ssa.assert_as_valid(mhd, x_Q, fssasig, ec, hf) invalid_sig = ec.p, sig[1] assert not ssa.verify(mhd, x_Q, invalid_sig) err_msg = "x-coordinate not in 0..p-1: " with pytest.raises(ValueError, match=err_msg): ssa.assert_as_valid(mhd, x_Q, invalid_sig, ec, hf) invalid_sig = sig[0], ec.p assert not ssa.verify(mhd, x_Q, invalid_sig) err_msg = "scalar s not in 0..n-1: " with pytest.raises(ValueError, match=err_msg): ssa.assert_as_valid(mhd, x_Q, invalid_sig, ec, hf) err_msg = "invalid size: 31 bytes instead of 32" with pytest.raises(ValueError, match=err_msg): ssa.sign(wrongmhd, q, None) err_msg = "private key not in 1..n-1: " with pytest.raises(ValueError, match=err_msg): ssa.sign(mhd, 0) # ephemeral key not in 1..n-1 err_msg = "private key not in 1..n-1: " with pytest.raises(ValueError, match=err_msg): ssa.sign(mhd, 1, 0) err_msg = "invalid zero challenge" with pytest.raises(ValueError, match=err_msg): ssa._recover_pubkey(0, sig[0], sig[1], ec) err_msg = "not a BIP340 public key" with pytest.raises(ValueError, match=err_msg): ssa._to_bip340_point(["not", "a BIP340", "public key"])
def test_signature() -> None: ec = CURVES["secp256k1"] msg = "Satoshi Nakamoto" q, x_Q = ssa.gen_keys(0x01) sig = ssa.sign(msg, q) ssa.assert_as_valid(msg, x_Q, sig) assert ssa.verify(msg, x_Q, sig) assert sig == ssa.deserialize(sig) ssa.assert_as_valid(msg, x_Q, sig) ssa.assert_as_valid(msg, x_Q, ssa.serialize(*sig)) ssa.assert_as_valid(msg, x_Q, ssa.serialize(*sig).hex()) msg_fake = "Craig Wright" assert not ssa.verify(msg_fake, x_Q, sig) err_msg = r"y_K is odd|signature verification failed" with pytest.raises(BTClibRuntimeError, match=err_msg): ssa.assert_as_valid(msg_fake, x_Q, sig) _, x_Q_fake = ssa.gen_keys(0x02) assert not ssa.verify(msg, x_Q_fake, sig) with pytest.raises(BTClibRuntimeError, match=err_msg): ssa.assert_as_valid(msg, x_Q_fake, sig) _, x_Q_fake = ssa.gen_keys(0x4) assert not ssa.verify(msg, x_Q_fake, sig) with pytest.raises(BTClibRuntimeError, match=err_msg): ssa.assert_as_valid(msg, x_Q_fake, sig) err_msg = "not a BIP340 public key" with pytest.raises(BTClibValueError, match=err_msg): ssa.assert_as_valid(msg, INF, sig) # type: ignore with pytest.raises(BTClibValueError, match=err_msg): ssa.point_from_bip340pubkey(INF) # type: ignore sig_fake = (sig[0], sig[1], sig[1]) assert not ssa.verify(msg, x_Q, sig_fake) # type: ignore err_msg = "too many values to unpack " with pytest.raises(ValueError, match=err_msg): ssa.assert_as_valid(msg, x_Q, sig_fake) # type: ignore sig_invalid = ec.p, sig[1] assert not ssa.verify(msg, x_Q, sig_invalid) err_msg = "x-coordinate not in 0..p-1: " with pytest.raises(BTClibValueError, match=err_msg): ssa.assert_as_valid(msg, x_Q, sig_invalid) sig_invalid = sig[0], ec.p assert not ssa.verify(msg, x_Q, sig_invalid) err_msg = "scalar s not in 0..n-1: " with pytest.raises(BTClibValueError, match=err_msg): ssa.assert_as_valid(msg, x_Q, sig_invalid) m_fake = b"\x00" * 31 err_msg = "invalid size: 31 bytes instead of 32" with pytest.raises(BTClibValueError, match=err_msg): ssa._assert_as_valid(m_fake, x_Q, sig) with pytest.raises(BTClibValueError, match=err_msg): ssa._sign(m_fake, q) err_msg = "private key not in 1..n-1: " with pytest.raises(BTClibValueError, match=err_msg): ssa.sign(msg, 0) # ephemeral key not in 1..n-1 err_msg = "private key not in 1..n-1: " with pytest.raises(BTClibValueError, match=err_msg): ssa._sign(reduce_to_hlen(msg, hf), q, 0) with pytest.raises(BTClibValueError, match=err_msg): ssa._sign(reduce_to_hlen(msg, hf), q, ec.n) err_msg = "invalid zero challenge" with pytest.raises(BTClibValueError, match=err_msg): ssa.__recover_pubkey(0, sig[0], sig[1], ec)
def test_musig(): """testing 3-of-3 MuSig https://github.com/ElementsProject/secp256k1-zkp/blob/secp256k1-zkp/src/modules/musig/musig.md https://blockstream.com/2019/02/18/musig-a-new-multisignature-standard/ https://eprint.iacr.org/2018/068 https://blockstream.com/2018/01/23/musig-key-aggregation-schnorr-signatures.html https://medium.com/@snigirev.stepan/how-schnorr-signatures-may-improve-bitcoin-91655bcb4744 """ ec = secp256k1 mhd = hf(b"message to sign").digest() # the signers private and public keys, # including both the curve Point and the BIP340-Schnorr public key q1, x_Q1 = ssa.gen_keys() x_Q1 = x_Q1.to_bytes(ec.psize, "big") q2, x_Q2 = ssa.gen_keys() x_Q2 = x_Q2.to_bytes(ec.psize, "big") q3, x_Q3 = ssa.gen_keys() x_Q3 = x_Q3.to_bytes(ec.psize, "big") # (non interactive) key setup # this is MuSig core: the rest is just Schnorr signature additivity # 1. lexicographic sorting of public keys keys: List[bytes] = list() keys.append(x_Q1) keys.append(x_Q2) keys.append(x_Q3) keys.sort() # 2. coefficients prefix = b"".join(keys) a1 = int_from_bits(hf(prefix + x_Q1).digest(), ec.nlen) % ec.n a2 = int_from_bits(hf(prefix + x_Q2).digest(), ec.nlen) % ec.n a3 = int_from_bits(hf(prefix + x_Q3).digest(), ec.nlen) % ec.n # 3. aggregated public key Q1 = mult(q1) Q2 = mult(q2) Q3 = mult(q3) Q = ec.add(double_mult(a1, Q1, a2, Q2), mult(a3, Q3)) if not ec.has_square_y(Q): # print("Q has been negated") a1 = ec.n - a1 # pragma: no cover a2 = ec.n - a2 # pragma: no cover a3 = ec.n - a3 # pragma: no cover # ready to sign: nonces and nonce commitments k1, _ = ssa.gen_keys() K1 = mult(k1) k2, _ = ssa.gen_keys() K2 = mult(k2) k3, _ = ssa.gen_keys() K3 = mult(k3) # exchange {K_i} (interactive) # computes s_i (non interactive) # WARNING: signers must exchange the nonces commitments {K_i} # before sharing {s_i} # same for all signers K = ec.add(ec.add(K1, K2), K3) if not ec.has_square_y(K): k1 = ec.n - k1 # pragma: no cover k2 = ec.n - k2 # pragma: no cover k3 = ec.n - k3 # pragma: no cover r = K[0] e = ssa._challenge(r, Q[0], mhd, ec, hf) s1 = (k1 + e * a1 * q1) % ec.n s2 = (k2 + e * a2 * q2) % ec.n s3 = (k3 + e * a3 * q3) % ec.n # exchange s_i (interactive) # finalize signature (non interactive) s = (s1 + s2 + s3) % ec.n sig = r, s # check signature is valid ssa.assert_as_valid(mhd, Q, sig, ec, hf) assert ssa.verify(mhd, Q, sig)