update python wrappers
diff --git a/python/amcl/core_utils.py b/python/amcl/core_utils.py
index 4b09d65..cfad037 100644
--- a/python/amcl/core_utils.py
+++ b/python/amcl/core_utils.py
@@ -197,6 +197,8 @@
 
     """
     random_value1, random_value1_val = make_octet(length)
+    _ = random_value1_val # Suppress warning
+
     _libamcl_core.generateRandom(rng, random_value1)
 
     random_value = to_str(random_value1)
diff --git a/python/amcl/factoring_zk.py b/python/amcl/factoring_zk.py
index d4eb3d6..32b8f3f 100644
--- a/python/amcl/factoring_zk.py
+++ b/python/amcl/factoring_zk.py
@@ -28,8 +28,8 @@
 
 _ffi = core_utils._ffi
 _ffi.cdef("""
-void FACTORING_ZK_prove(csprng *RNG, octet *P, octet *Q, octet *R, octet *E, octet *Y);
-int FACTORING_ZK_verify(octet *N, octet *E, octet *Y);
+void FACTORING_ZK_prove(csprng *RNG, octet *P, octet *Q, octet *ID, octet *AD, octet *R, octet *E, octet *Y);
+int FACTORING_ZK_verify(octet *N, octet *E, octet *Y, octet *ID, octet *AD);
 """)
 
 if (platform.system() == 'Windows'):
@@ -51,7 +51,7 @@
 FAIL = 91
 
 
-def prove(rng, p, q, r=None):
+def prove(rng, p, q, id, ad=None, r=None):
     """Generate factoring knowledge proof
 
 
@@ -62,6 +62,8 @@
               number generator instance
         p   : First prime factor of n. HFS_2048 bytes long
         q   : Second prime factor of n. HFS_2048 bytes long
+        id  : Unique identifier of the prover
+        ad  : Additional data to bind in the proof. Optional
         r   : Deterministic value for r. FS_2048 bytes long
 
     Returns::
@@ -79,13 +81,20 @@
         _ = r_val # Suppress warning
         rng = _ffi.NULL
 
-    p_oct, p_val = core_utils.make_octet(None, p)
-    q_oct, q_val = core_utils.make_octet(None, q)
-    e_oct, e_val = core_utils.make_octet(B)
-    y_oct, y_val = core_utils.make_octet(FS_2048)
-    _ = p_val, q_val, e_val, y_val # Suppress warnings
+    if ad is None:
+        ad_oct = _ffi.NULL
+    else:
+        ad_oct, ad_val = core_utils.make_octet(None, ad)
+        _ = ad_val # Suppress warning
 
-    _libamcl_mpc.FACTORING_ZK_prove(rng, p_oct, q_oct, r_oct, e_oct, y_oct)
+    p_oct, p_val   = core_utils.make_octet(None, p)
+    q_oct, q_val   = core_utils.make_octet(None, q)
+    e_oct, e_val   = core_utils.make_octet(B)
+    y_oct, y_val   = core_utils.make_octet(FS_2048)
+    id_oct, id_val = core_utils.make_octet(None, id)
+    _ = p_val, q_val, e_val, y_val, id_val # Suppress warnings
+
+    _libamcl_mpc.FACTORING_ZK_prove(rng, p_oct, q_oct, id_oct, ad_oct, r_oct, e_oct, y_oct)
 
     # Clear memory
     core_utils.clear_octet(p_oct)
@@ -94,7 +103,7 @@
     return core_utils.to_str(e_oct), core_utils.to_str(y_oct)
 
 
-def verify(n, e, y):
+def verify(n, e, y, id, ad=None):
     """Verify knowledge of factoring proof
 
     Args::
@@ -102,6 +111,8 @@
         n : public modulus
         e : First component of the factoring proof. B bytes long
         y : Second component of the factoring proof. FS_2048 bytes long
+        id  : Unique identifier of the prover
+        ad  : Additional data to bind in the challenge. Optional
 
     Returns::
 
@@ -110,11 +121,18 @@
     Raises:
 
     """
-    n_oct, n_val = core_utils.make_octet(None, n)
-    e_oct, e_val = core_utils.make_octet(None, e)
-    y_oct, y_val = core_utils.make_octet(None, y)
-    _ = n_val, e_val, y_val # Suppress warning
+    if ad is None:
+        ad_oct = _ffi.NULL
+    else:
+        ad_oct, ad_val = core_utils.make_octet(None, ad)
+        _ = ad_val # Suppress warning
 
-    rc = _libamcl_mpc.FACTORING_ZK_verify(n_oct, e_oct, y_oct)
+    n_oct, n_val   = core_utils.make_octet(None, n)
+    e_oct, e_val   = core_utils.make_octet(None, e)
+    y_oct, y_val   = core_utils.make_octet(None, y)
+    id_oct, id_val = core_utils.make_octet(None, id)
+    _ = n_val, e_val, y_val, id_val # Suppress warning
+
+    rc = _libamcl_mpc.FACTORING_ZK_verify(n_oct, e_oct, y_oct, id_oct, ad_oct)
 
     return rc
diff --git a/python/amcl/schnorr.py b/python/amcl/schnorr.py
index d6c462c..49b57d3 100644
--- a/python/amcl/schnorr.py
+++ b/python/amcl/schnorr.py
@@ -30,7 +30,7 @@
 _ffi.cdef("""
 extern void SCHNORR_random_challenge(csprng *RNG, octet *E);
 extern void SCHNORR_commit(csprng *RNG, octet *R, octet *C);
-extern void SCHNORR_challenge(const octet *V, const octet *C, octet *E);
+extern void SCHNORR_challenge(const octet *V, const octet *C, octet *ID, octet *AD, octet *E);
 extern void SCHNORR_prove(const octet *R, const octet *E, const octet *X, octet *P);
 extern int SCHNORR_verify(octet *V, octet *C, const octet *E, const octet *P);
 """)
@@ -121,7 +121,7 @@
     return r, core_utils.to_str(C)
 
 
-def challenge(V, C):
+def challenge(V, C, ID, AD=None):
     """Generate a deterministic challenge for the Schnorr's Proof
 
     Generates a deterministic value r in [0, .., q] suitable as a
@@ -130,8 +130,10 @@
 
     Args::
 
-        V : Public ECP of the DLOG. V = x.G
-        C : Commitment for the Schnorr's Proof
+        V  : Public ECP of the DLOG. V = x.G
+        C  : Commitment for the Schnorr's Proof
+        ID : Unique idenitifier of the prover
+        AD : Additional data to bind in the challenge. Optional
 
     Returns::
 
@@ -140,14 +142,21 @@
     Raises:
 
     """
-    V_oct, V_val = core_utils.make_octet(None, V)
-    C_oct, C_val = core_utils.make_octet(None, C)
-    _ = V_val, C_val # Suppress warning
+    if AD is None:
+        AD_oct = _ffi.NULL
+    else:
+        AD_oct, AD_val = core_utils.make_octet(None, AD)
+        _ = AD_val # Suppress warning
+
+    V_oct, V_val   = core_utils.make_octet(None, V)
+    C_oct, C_val   = core_utils.make_octet(None, C)
+    ID_oct, ID_val = core_utils.make_octet(None, ID)
+    _ = V_val, C_val, ID_val # Suppress warning
 
     e, e_val = core_utils.make_octet(EGS)
     _ = e_val # Suppress warning
 
-    _libamcl_mpc.SCHNORR_challenge(V_oct, C_oct, e)
+    _libamcl_mpc.SCHNORR_challenge(V_oct, C_oct, ID_oct, AD_oct, e)
 
     return core_utils.to_str(e)
 
diff --git a/python/benchmark/bench_schnorr.py b/python/benchmark/bench_schnorr.py
index ba95584..e70e68c 100755
--- a/python/benchmark/bench_schnorr.py
+++ b/python/benchmark/bench_schnorr.py
@@ -30,14 +30,18 @@
 x_hex = "fab4ce512dff74bd9c71c89a14de5b877af45dca0329ee3fcb72611c0784fef3"
 V_hex = "032cf4b348c9d00718f01ed98923e164df53b5e8bc4c2250662ed2df784e1784f4"
 
+ID = b"unique_user_identifier"
+AD_hex = "d7d3155616778fb436a1eb2070892205" 
+
 if __name__ == "__main__":
-    r = bytes.fromhex(r_hex)
-    x = bytes.fromhex(x_hex)
-    V = bytes.fromhex(V_hex)
+    r  = bytes.fromhex(r_hex)
+    x  = bytes.fromhex(x_hex)
+    V  = bytes.fromhex(V_hex)
+    AD = bytes.fromhex(AD_hex)
 
     # Generate quantities for benchmark
     r, C = schnorr.commit(None, r)
-    e = schnorr.challenge(V, C)
+    e = schnorr.challenge(V, C, ID, AD=AD)
     p = schnorr.prove(r, e, x)
 
     # Check consistency of the generated quantities
@@ -47,7 +51,7 @@
     fncall = lambda: schnorr.commit(None, r)
     time_func("commit   ", fncall, unit="us")
 
-    fncall = lambda: schnorr.challenge(V, C)
+    fncall = lambda: schnorr.challenge(V, C, ID, AD=AD)
     time_func("challenge", fncall, unit="us")
 
     fncall = lambda: schnorr.prove(r, e, x)
diff --git a/python/benchmark/bench_zk_factoring.py b/python/benchmark/bench_zk_factoring.py
index f9ab584..342f067 100755
--- a/python/benchmark/bench_zk_factoring.py
+++ b/python/benchmark/bench_zk_factoring.py
@@ -31,19 +31,23 @@
 n_hex = "c0870b552afb6c8c09f79e39ad6ca17ca93085c2cd7a726ade69574961ff9ce8ad33c7dda2e0703a3b0010c2e5bb7552c74164ce8dd011d85e5969090df53fe10e39cbe530704da32ff07228a6b6da34a5929e8a231c3080d812dc6e93affd81682339a6aee192927c582da8941bebf46e13c4ea3918a1477951fa66d367e70d8551b1869316d48317e0702d7bce242a326000f3dc763c44eba2044a1df713a94c1339edd464b145dcadf94e6e61be73dc270c878e1a28be720df2209202d00e101c3b255b757eaf547acd863d51eb676b851511b3dadeda926714719dceddd3af7908893ae65f2b95ee5c4d36cc6862cbe6886a62d7c1e2d0db48c399a6d44b"
 r_hex = "c05f6c79e81fab2f1aa6af48dc5afa89a21c0aee03e93944cacfefef1be90f41ec8c2055760beafa9ed87dd67dbd56b33a2568dfec62a03f06c4f8449a93eee858507f4b602bf305e1c9968d9f5b6dc3120c27e053a1d7e51590e0bacb8d36c27bccce1a57c1e3aeb0832905d4e2bb8eaee883b4df042d8660cf3e0c9777b6be34c18bef02347f92cb71f372f61c018860211932dd46de8f925212d7afe6dd2f3cda05f8d5a6bd1b138b66c5efd7fca31f926c721f6d4207b97fc01cdf325da21233f6df37adbcd67472b332f7490a4a96e0fef31beef55b9446067b8e8d807384e3d31051c7a1f27296a6ae111b30c3d1f3f81666fd9ad99df531bb68428029"
 
+uid = b"unique_user_identifier"
+ad_hex = "d7d3155616778fb436a1eb2070892205" 
+
 if __name__ == "__main__":
-    p = bytes.fromhex(p_hex)
-    q = bytes.fromhex(q_hex)
-    n = bytes.fromhex(n_hex)
-    r = bytes.fromhex(r_hex)
+    p =  bytes.fromhex(p_hex)
+    q =  bytes.fromhex(q_hex)
+    n =  bytes.fromhex(n_hex)
+    r =  bytes.fromhex(r_hex)
+    ad = bytes.fromhex(ad_hex)
 
     # Generate quantities for benchmark
-    e, y = factoring_zk.prove(None, p, q, r)
-    assert factoring_zk.verify(n, e, y) == factoring_zk.OK
+    e, y = factoring_zk.prove(None, p, q, uid, ad=ad, r=r)
+    assert factoring_zk.verify(n, e, y, uid, ad=ad) == factoring_zk.OK
 
     # Run benchmark
-    fncall = lambda: factoring_zk.prove(None, p, q, r)
+    fncall = lambda: factoring_zk.prove(None, p, q, uid, ad=ad, r=r)
     time_func("prove ", fncall)
 
-    fncall = lambda: factoring_zk.verify(n, e, y)
+    fncall = lambda: factoring_zk.verify(n, e, y, uid, ad=ad)
     time_func("verify", fncall)
diff --git a/python/examples/example_full.py b/python/examples/example_full.py
index cece81b..f8b0448 100755
--- a/python/examples/example_full.py
+++ b/python/examples/example_full.py
@@ -66,7 +66,7 @@
     return key_material
 
 
-def generate_key_material_zkp(rng, key_material):
+def generate_key_material_zkp(rng, key_material, ID, AD=None):
     """ Generate ZK Proofs for key material
 
     Generate a commitment to the ECDSA PK, a Schnorr's
@@ -98,19 +98,19 @@
 
     # Generate Schnorr's proof for ECDSA PK
     sr, sc = schnorr.commit(rng)
-    e = schnorr.challenge(key_material['ecdsa_pk'], sc)
+    e = schnorr.challenge(key_material['ecdsa_pk'], sc, ID, AD=AD)
     sp = schnorr.prove(sr, e, key_material['ecdsa_sk'])
 
     # Generate ZKP of knowledge of factorization for
     # Paillier key pair
     psk_p, psk_q = mpc.mpc_dump_paillier_sk(key_material['paillier_sk'])
 
-    fe, fy = factoring_zk.prove(rng, psk_p, psk_q)
+    fe, fy = factoring_zk.prove(rng, psk_p, psk_q, ID, ad=AD)
 
     return r, c, sc, sp, fe, fy
 
 
-def verify_key_material(key_material, r, c, sc, sp, fe, fy, player):
+def verify_key_material(key_material, r, c, sc, sp, fe, fy, player, ID, AD=None):
     """ Verify key material
 
     Verify the conunterparty key material using the
@@ -137,13 +137,13 @@
     assert rc == commitments.OK, f"[{player}] Failure decommitting ecdsa_pk. rc {rc}"
 
     # Verify ECDSA PK Schnorr's proof
-    e = schnorr.challenge(key_material['ecdsa_pk'], sc)
+    e = schnorr.challenge(key_material['ecdsa_pk'], sc, ID, AD=AD)
     rc = schnorr.verify(key_material['ecdsa_pk'], sc, e, sp)
     assert rc == schnorr.OK, f"[{player}] Invalid ECDSA PK Schnorr Proof. rc {rc}"
 
     # Verify factoring ZKP
     n = mpc.paillier_pk_to_octet(key_material['paillier_pk'])
-    rc = factoring_zk.verify(n, fe, fy)
+    rc = factoring_zk.verify(n, fe, fy, ID, ad=AD)
     assert rc == factoring_zk.OK, f"[{player}] Invalid Factoring ZKP. rc {rc}"
 
 
@@ -152,6 +152,9 @@
     rng = core_utils.create_csprng(seed)
 
 
+    alice_id = b"alice_unique_identifier"
+    bob_id   = b"bob_unique_identifier"
+
     ### Key setup ###
 
     print("Setup Key Material\n")
@@ -160,12 +163,16 @@
     key_material1 = generate_key_material(rng, "Alice")
     key_material2 = generate_key_material(rng, "Bob")
 
+    # Generate and exchange nonce for liveliness
+    alice_ad = core_utils.generate_random(rng, 32)
+    bob_ad   = core_utils.generate_random(rng, 32)
+
     print("[Alice] Generate ECDSA and Paillier key pairs")
     print("[Bob] Generate ECDSA and Paillier key pairs")
 
     # Generate key material ZKP
-    r1, c1, sc1, sp1, fe1, fy1 = generate_key_material_zkp(rng, key_material1)
-    r2, c2, sc2, sp2, fe2, fy2 = generate_key_material_zkp(rng, key_material2)
+    r1, c1, sc1, sp1, fe1, fy1 = generate_key_material_zkp(rng, key_material1, alice_id, AD = alice_ad)
+    r2, c2, sc2, sp2, fe2, fy2 = generate_key_material_zkp(rng, key_material2, bob_id,   AD = bob_ad)
 
     print("[Alice] Generate commitment to ECDSA PK and ZKPs")
     print("[Bob] Generate commitment to ECDSA PK and ZKPs")
@@ -190,10 +197,10 @@
     }
 
     print("[Alice] Verify ZKP")
-    verify_key_material(c_key_material1, r2, c2, sc2, sp2, fe2, fy2, "Alice")
+    verify_key_material(c_key_material1, r2, c2, sc2, sp2, fe2, fy2, "Alice", bob_id, AD=bob_ad)
 
     print("[Bob] Verify ZKP")
-    verify_key_material(c_key_material2, r1, c1, sc1, sp1, fe1, fy1, "Bob")
+    verify_key_material(c_key_material2, r1, c1, sc1, sp1, fe1, fy1, "Bob", alice_id, AD=alice_ad)
 
     # Recombine full ECDSA PK
     rc, ecdsa_full_pk1 = mpc.mpc_sum_pk(key_material1['ecdsa_pk'], c_key_material1['ecdsa_pk'])
@@ -208,7 +215,7 @@
     # Message
     M = b'test message'
 
-    print(f"\nSign message '{M.encode('utf-8')}'")
+    print(f"\nSign message '{M.decode('utf-8')}'")
 
     # Generate k, gamma and gamma.G
     print("[Alice] Generate k, gamma and gamma.G")
@@ -219,13 +226,17 @@
     GAMMA2, gamma2 = mpc.mpc_ecdsa_key_pair_generate(rng)
     k2 = mpc.mpc_k_generate(rng)
 
-    ## Commit to GAMMA1, GAMMA2
+    ## Commit to GAMMA1, GAMMA2 and exchange nonces for liveliness
     print("[Alice] Commit to GAMMA1")
     GAMMAR1, GAMMAC1 = commitments.nm_commit(rng, GAMMA1)
 
+    bob_ad = core_utils.generate_random(rng, 32)
+
     print("[Bob] Commit to GAMMA2")
     GAMMAR2, GAMMAC2 = commitments.nm_commit(rng, GAMMA2)
 
+    alice_ad = core_utils.generate_random(rng, 32)
+
     ## Engage in MTA with k_i, gamma_j
 
     # k1, gamma2
@@ -275,12 +286,12 @@
     # Generate Schnorr's Proofs
     print("[Alice] Generate Schnorr's Proof")
     GAMMA_schnorr_r1, GAMMA_schnorr_c1 = schnorr.commit(rng)
-    GAMMA_schnorr_e1 = schnorr.challenge(GAMMA1, GAMMA_schnorr_c1)
+    GAMMA_schnorr_e1 = schnorr.challenge(GAMMA1, GAMMA_schnorr_c1, alice_id, AD = alice_ad)
     GAMMA_schnorr_p1 = schnorr.prove(GAMMA_schnorr_r1, GAMMA_schnorr_e1, gamma1)
 
     print("[Bob] Generate Schnorr's Proof")
     GAMMA_schnorr_r2, GAMMA_schnorr_c2 = schnorr.commit(rng)
-    GAMMA_schnorr_e2 = schnorr.challenge(GAMMA2, GAMMA_schnorr_c2)
+    GAMMA_schnorr_e2 = schnorr.challenge(GAMMA2, GAMMA_schnorr_c2, bob_id, AD = bob_ad)
     GAMMA_schnorr_p2 = schnorr.prove(GAMMA_schnorr_r2, GAMMA_schnorr_e2, gamma2)
 
     print("[Alice] Transmit decommitment and Schnorr Proof for GAMMA1")
@@ -290,14 +301,14 @@
     rc = commitments.nm_decommit(GAMMA2, GAMMAR2, GAMMAC2)
     assert rc == commitments.OK, f'[Alice] Error decommitting GAMMA2. rc {rc}'
 
-    GAMMA_schnorr_e2 = schnorr.challenge(GAMMA2, GAMMA_schnorr_c2)
+    GAMMA_schnorr_e2 = schnorr.challenge(GAMMA2, GAMMA_schnorr_c2, bob_id, AD=bob_ad)
     rc = schnorr.verify(GAMMA2, GAMMA_schnorr_c2, GAMMA_schnorr_e2, GAMMA_schnorr_p2)
     assert rc == schnorr.OK, f'[Alice] Error verifying Schnorr proof for GAMMA2'
 
     rc = commitments.nm_decommit(GAMMA1, GAMMAR1, GAMMAC1)
     assert rc == commitments.OK, f'[Bob] Error decommitting GAMMA1. rc {rc}'
 
-    GAMMA_schnorr_e1 = schnorr.challenge(GAMMA1, GAMMA_schnorr_c1)
+    GAMMA_schnorr_e1 = schnorr.challenge(GAMMA1, GAMMA_schnorr_c1, alice_id, AD=alice_ad)
     rc = schnorr.verify(GAMMA1, GAMMA_schnorr_c1, GAMMA_schnorr_e1, GAMMA_schnorr_p1)
     assert rc == schnorr.OK, f'[Bob] Error verifying Schnorr proof for GAMMA1'
 
diff --git a/python/examples/example_schnorr.py b/python/examples/example_schnorr.py
index 0e12cd2..b6592d4 100755
--- a/python/examples/example_schnorr.py
+++ b/python/examples/example_schnorr.py
@@ -31,19 +31,25 @@
 x_hex = "fab4ce512dff74bd9c71c89a14de5b877af45dca0329ee3fcb72611c0784fef3"
 V_hex = "032cf4b348c9d00718f01ed98923e164df53b5e8bc4c2250662ed2df784e1784f4"
 
+ID = b"unique_user_identifier"
+
 if __name__ == "__main__":
     seed = bytes.fromhex(seed_hex)
 
-    x = bytes.fromhex(x_hex)
-    V = bytes.fromhex(V_hex)
+    x  = bytes.fromhex(x_hex)
+    V  = bytes.fromhex(V_hex)
 
     # random number generator
     rng = core_utils.create_csprng(seed)
 
+    AD = core_utils.generate_random(rng, 32)
+
     print("Example Schnorr Protocol")
     print("DLOG: V = x.G")
-    print(f"\tx = {x_hex}")
-    print(f"\tV = {V_hex}")
+    print(f"\tx  = {x_hex}")
+    print(f"\tV  = {V_hex}")
+    print(f"\tID = {ID.decode('utf-8')}")
+    print(f"\tAD = {AD.hex()}")
 
     # Generate commitment C = r.G, r random in [0, ..., q]
     r, C = schnorr.commit(rng)
@@ -52,10 +58,10 @@
     print(f"\tr = {r.hex()}")
     print(f"\tC = {C.hex()}")
 
-    # Generate deterministic challenge e = H(G, V, C)
-    e = schnorr.challenge(V, C)
+    # Generate deterministic challenge e = H(G, V, C, ID, AD)
+    e = schnorr.challenge(V, C, ID, AD = AD)
 
-    print("\n[Prover] Deterministic Challenge e = H(G, V, C)")
+    print("\n[Prover] Deterministic Challenge e = H(G, V, C, ID, AD)")
     print(f"\te = {e.hex()}")
 
     # Generate proof p = r - ex mod q
@@ -65,8 +71,8 @@
     print(f"\tp = {p.hex()}")
 
     # Verifier regenerates deterministic challenge
-    e = schnorr.challenge(V, C)
-    print("\n[Verifier] Deterministic Challenge e = H(G, V, C)")
+    e = schnorr.challenge(V, C, ID, AD = AD)
+    print("\n[Verifier] Deterministic Challenge e = H(G, V, C, ID, AD)")
     print(f"\te = {e.hex()}")
 
     # Verify
diff --git a/python/examples/example_zk_factoring.py b/python/examples/example_zk_factoring.py
index 08d6f6a..bf80508 100755
--- a/python/examples/example_zk_factoring.py
+++ b/python/examples/example_zk_factoring.py
@@ -32,6 +32,8 @@
 q_hex = "dbffe278edd44c2655714e5a4cc82e66e46063f9ab69df9d0ed20eb3d7f2d8c7d985df71c28707f32b961d160ca938e9cf909cd77c4f8c630aec34b67714cbfd4942d7147c509db131bc2d6a667eb30df146f64b710f8f5247848b0a75738a38772e31014fd63f0b769209928d586499616dcc90700b393156e12eea7e15a835"
 n_hex = "c0870b552afb6c8c09f79e39ad6ca17ca93085c2cd7a726ade69574961ff9ce8ad33c7dda2e0703a3b0010c2e5bb7552c74164ce8dd011d85e5969090df53fe10e39cbe530704da32ff07228a6b6da34a5929e8a231c3080d812dc6e93affd81682339a6aee192927c582da8941bebf46e13c4ea3918a1477951fa66d367e70d8551b1869316d48317e0702d7bce242a326000f3dc763c44eba2044a1df713a94c1339edd464b145dcadf94e6e61be73dc270c878e1a28be720df2209202d00e101c3b255b757eaf547acd863d51eb676b851511b3dadeda926714719dceddd3af7908893ae65f2b95ee5c4d36cc6862cbe6886a62d7c1e2d0db48c399a6d44b"
 
+uid = b"unique_user_identifier"
+
 if __name__ == "__main__":
     seed = bytes.fromhex(seed_hex)
     rng = core_utils.create_csprng(seed)
@@ -40,21 +42,25 @@
     q = bytes.fromhex(q_hex)
     n = bytes.fromhex(n_hex)
 
+    ad = core_utils.generate_random(rng, 32)
+
     print("Example ZK Proof of Knowledge of factoring")
     print("Parameters")
-    print(f"\tP = {p.hex()}")
-    print(f"\tQ = {q.hex()}")
-    print(f"\tN = {n.hex()}")
+    print(f"\tP  = {p.hex()}")
+    print(f"\tQ  = {q.hex()}")
+    print(f"\tN  = {n.hex()}")
+    print(f"\tID = {uid.decode('utf-8')}")
+    print(f"\tAD = {ad.hex()}")
 
     # Prove
-    e, y = factoring_zk.prove(rng, p, q, None)
+    e, y = factoring_zk.prove(rng, p, q, uid, ad=ad)
 
     print("\nGenerate proof")
     print(f"\tE = {e.hex()}")
     print(f"\tY = {y.hex()}")
 
     # Verify
-    ec = factoring_zk.verify(n, e, y)
+    ec = factoring_zk.verify(n, e, y, uid, ad=ad)
 
     print("\nVerify proof")
     if ec == factoring_zk.OK:
diff --git a/python/test/CMakeLists.txt b/python/test/CMakeLists.txt
index ced24cd..77def9b 100644
--- a/python/test/CMakeLists.txt
+++ b/python/test/CMakeLists.txt
@@ -60,9 +60,9 @@
 if(NOT CMAKE_BUILD_TYPE STREQUAL "ASan")
   add_python_test(test_python_aes              test_aes.py)
   add_python_test(test_python_mpc_mta          test_mta.py)
+  add_python_test(test_python_mpc_ecdsa        test_ecdsa.py)
   add_python_test(test_python_mpc_r            test_r.py)
   add_python_test(test_python_mpc_s            test_s.py)
-  add_python_test(test_python_mpc_ecdsa        test_ecdsa.py)
   add_python_test(test_python_mpc_schnorr      test_schnorr.py)
   add_python_test(test_python_mpc_nm_commit    test_nm_commit.py)
   add_python_test(test_python_mpc_zk_factoring test_zk_factoring.py)
diff --git a/python/test/test_schnorr.py b/python/test/test_schnorr.py
index 766c37f..9423265 100755
--- a/python/test/test_schnorr.py
+++ b/python/test/test_schnorr.py
@@ -78,12 +78,17 @@
         """ Test using test vectors """
 
         for vector in self.tv:
-            V = bytes.fromhex(vector["V"])
-            C = bytes.fromhex(vector["C"])
+            V  = bytes.fromhex(vector["V"])
+            C  = bytes.fromhex(vector["C"])
+            ID = bytes.fromhex(vector["ID"])
+            AD = bytes.fromhex(vector["AD"])
+
+            if not AD:
+                AD = None
 
             e_golden = bytes.fromhex(vector["E"])
 
-            e = schnorr.challenge(V, C)
+            e = schnorr.challenge(V, C, ID, AD=AD)
 
             self.assertEqual(e, e_golden)
 
diff --git a/python/test/test_zk_factoring.py b/python/test/test_zk_factoring.py
index 7218de5..42f2ea0 100755
--- a/python/test/test_zk_factoring.py
+++ b/python/test/test_zk_factoring.py
@@ -30,11 +30,21 @@
 
 seed_hex = "78d0fb6705ce77dee47d03eb5b9c5d30"
 
-p_hex = "e008507e09c24d756280f3d94912fb9ac16c0a8a1757ee01a350736acfc7f65880f87eca55d6680253383fc546d03fd9ebab7d8fa746455180888cb7c17edf58d3327296468e5ab736374bc9a0fa02606ed5d3a4a5fb1677891f87fbf3c655c3e0549a86b17b7ddce07c8f73e253105e59f5d3ed2c7ba5bdf8495df40ae71a7f"
-q_hex = "dbffe278edd44c2655714e5a4cc82e66e46063f9ab69df9d0ed20eb3d7f2d8c7d985df71c28707f32b961d160ca938e9cf909cd77c4f8c630aec34b67714cbfd4942d7147c509db131bc2d6a667eb30df146f64b710f8f5247848b0a75738a38772e31014fd63f0b769209928d586499616dcc90700b393156e12eea7e15a835"
+p_hex  = "e008507e09c24d756280f3d94912fb9ac16c0a8a1757ee01a350736acfc7f65880f87eca55d6680253383fc546d03fd9ebab7d8fa746455180888cb7c17edf58d3327296468e5ab736374bc9a0fa02606ed5d3a4a5fb1677891f87fbf3c655c3e0549a86b17b7ddce07c8f73e253105e59f5d3ed2c7ba5bdf8495df40ae71a7f"
+q_hex  = "dbffe278edd44c2655714e5a4cc82e66e46063f9ab69df9d0ed20eb3d7f2d8c7d985df71c28707f32b961d160ca938e9cf909cd77c4f8c630aec34b67714cbfd4942d7147c509db131bc2d6a667eb30df146f64b710f8f5247848b0a75738a38772e31014fd63f0b769209928d586499616dcc90700b393156e12eea7e15a835"
+id_str = "unique_identifier"
+ad_hex = "d7d3155616778fb436a1eb2070892205"
 
-e_hex = "32c670610e73c428785944ab7b582371"
-y_hex = "b4ebebd6177b2eb04149aa463ede7ba2216657e3b4de42f496c0d493b4d734131e63edcde042d951b9bf285622b9d69e9ee170156deeb173725032a952068e68b18f69bd4e52677d48d846055988877ce9e97b962f01e3f425f3101a6a589f020c858b1ee5ae8f79e4c63ce2356d8a9aa703100b3b3588d0aae7d7857b672d1beb25afc90a93045837aca1c39511816d4fc84ad0db35edf9adac810c46965868e79a5eb9509f9d7c315c5439daf561b312c0dd276263464409aef75a65c157277ba0bcef2cb1929995ba6749a8c54187cf2a9cfc9febc40bee8b149973590f9d34ae8c79111792e92b5fcdbd993f6ce8ad1558f5f8e691c3ce2ca9b2c15f599c"
+e_hex = "5812ec82060d2a3eab3c967a2d710223"
+y_hex = "b4ebebd6177b2eb04149aa463ede7ba2216657e3b4de42f496c0d493b4d734131e63edcde042d951b9bf285622b9d69e9ee170156deeb173725032a952068e68b18f69bd4e52677d48d846055988877ce9e97b962f01e3f425f3101a6a589f020c858b1ee5ae8f79e4c63ce2356d8a9ae7b4e925357bc7aa2fb609c7cb6578b209f3993868c4e569dafeed4bc0a590453b6159819eed28dc7d2d25011641fa7312076813d85751fd23c75f5182e0c97d2fc8dce58d53a5ec2df3ec2ba08a063f6f9de97a8174c442acbd627dd864df41d1e322267c5674bd5459a3f12afad2b26fe5116a110b243cf8a78ce61d8eed22afde7f8d27a7c4b37fc07d55e8bdf412"
+
+
+def process_tv(vector):
+    for key, val in vector.items():
+        if key != "TEST":
+            vector[key] = bytes.fromhex(val)
+    
+    return vector
 
 
 class TestProve(unittest.TestCase):
@@ -45,24 +55,27 @@
         seed = bytes.fromhex(seed_hex)
         self.rng = core_utils.create_csprng(seed)
 
-        self.p = bytes.fromhex(p_hex)
-        self.q = bytes.fromhex(q_hex)
-        self.e = bytes.fromhex(e_hex)
-        self.y = bytes.fromhex(y_hex)
+        self.p  = bytes.fromhex(p_hex)
+        self.q  = bytes.fromhex(q_hex)
+        self.e  = bytes.fromhex(e_hex)
+        self.y  = bytes.fromhex(y_hex)
+        self.id = id_str.encode('utf-8')
+        self.ad = bytes.fromhex(ad_hex)
 
         with open("factoring_zk/prove.json", "r") as f:
             self.tv = json.load(f)
 
         for vector in self.tv:
-            for key, val in vector.items():
-                if key != "TEST":
-                    vector[key] = bytes.fromhex(val)
+            process_tv(vector)
 
     def test_tv(self):
         """ test using test vectors """
 
         for vector in self.tv:
-            e, y = factoring_zk.prove(None, vector['P'], vector['Q'], vector['R'])
+            if not vector['AD']:
+                vector['AD'] = None
+
+            e, y = factoring_zk.prove(None, vector['P'], vector['Q'], vector['ID'], ad=vector['AD'], r=vector['R'])
 
             self.assertEqual(e, vector['E'])
             self.assertEqual(y, vector['Y'])
@@ -70,7 +83,7 @@
     def test_random(self):
         """ test using PRNG """
 
-        e, y = factoring_zk.prove(self.rng, self.p, self.q)
+        e, y = factoring_zk.prove(self.rng, self.p, self.q, self.id, ad=self.ad)
 
         self.assertEqual(e, self.e)
         self.assertEqual(y, self.y)
@@ -83,22 +96,23 @@
             self.tv = json.load(f)
 
         for vector in self.tv:
-            for key, val in vector.items():
-                if key != "TEST":
-                    vector[key] = bytes.fromhex(val)
+            process_tv(vector)
 
     def test_tv(self):
         """ test using test vectors """
 
         for vector in self.tv:
-            ec = factoring_zk.verify(vector['N'], vector['E'], vector['Y'])
+            if not vector['AD']:
+                vector['AD'] = None
+
+            ec = factoring_zk.verify(vector['N'], vector['E'], vector['Y'], vector['ID'], ad=vector['AD'])
 
             self.assertEqual(ec, factoring_zk.OK)
 
     def test_failure(self):
         """ Test error codes are propagated correctly """
 
-        ec = factoring_zk.verify(self.tv[0]['Y'], self.tv[0]['E'], self.tv[0]['N'])
+        ec = factoring_zk.verify(self.tv[0]['Y'], self.tv[0]['E'], self.tv[0]['N'], self.tv[0]['ID'])
 
         self.assertEqual(ec, factoring_zk.FAIL)