Merge branch 'develop' into add-full-example

Conflicts:
	.gitignore
diff --git a/.gitignore b/.gitignore
index 35fd25b..37748a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,4 +8,3 @@
 vagrant/.vagrant
 coverage
 examples/scratch.c
-
diff --git a/examples/example_ecdsa.c b/examples/example_ecdsa.c
index d546ab7..be43f53 100644
--- a/examples/example_ecdsa.c
+++ b/examples/example_ecdsa.c
@@ -297,7 +297,7 @@
     printf("\n");
 
     // Calculate the R signature component
-    rc = MPC_R(&INVKGAMMA, &GAMMAPT1, &GAMMAPT2, &SIG_R);
+    rc = MPC_R(&INVKGAMMA, &GAMMAPT1, &GAMMAPT2, &SIG_R, NULL);
     if (rc)
     {
         fprintf(stderr, "FAILURE MPC_R rc: %d\n", rc);
diff --git a/examples/example_full.c b/examples/example_full.c
new file mode 100644
index 0000000..55eabb6
--- /dev/null
+++ b/examples/example_full.c
@@ -0,0 +1,1649 @@
+/*
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+*/
+
+#include <string.h>
+#include "amcl/mta.h"
+#include "amcl/mpc.h"
+#include "amcl/commitments.h"
+#include "amcl/factoring_zk.h"
+#include "amcl/schnorr.h"
+
+/* Example of the full flow */
+
+typedef struct
+{
+    PAILLIER_private_key paillier_sk;
+    PAILLIER_public_key  paillier_pk;
+    PAILLIER_public_key  paillier_cpk;
+
+    COMMITMENTS_BC_priv_modulus bc_sm;
+    COMMITMENTS_BC_pub_modulus  bc_pm;
+    COMMITMENTS_BC_pub_modulus  bc_cpm;
+
+    octet *SK;
+    octet *PK;
+    octet *CPK;
+    octet *FPK;
+} key_material;
+
+// Safe primes for BC Setup
+char *A_P_hex = "e008507e09c24d756280f3d94912fb9ac16c0a8a1757ee01a350736acfc7f65880f87eca55d6680253383fc546d03fd9ebab7d8fa746455180888cb7c17edf58d3327296468e5ab736374bc9a0fa02606ed5d3a4a5fb1677891f87fbf3c655c3e0549a86b17b7ddce07c8f73e253105e59f5d3ed2c7ba5bdf8495df40ae71a7f";
+char *A_Q_hex = "dbffe278edd44c2655714e5a4cc82e66e46063f9ab69df9d0ed20eb3d7f2d8c7d985df71c28707f32b961d160ca938e9cf909cd77c4f8c630aec34b67714cbfd4942d7147c509db131bc2d6a667eb30df146f64b710f8f5247848b0a75738a38772e31014fd63f0b769209928d586499616dcc90700b393156e12eea7e15a835";
+
+char *B_P_hex = "efa013403e9ea93daf97f1dd4b42eba602410e048852b20cd448d51793ac2ee725e79eaac82d22cdd6cfb966cba62904a26da47d7a6085fba194e24eddbc92f66a0bd990c8cb9abf98fff48d52d33215d68f6f030cd9440f85987b2ab44332646ea38bc218fedc83a24cf57b7615c0fc9289778f7ba60f4ed71c7c3c571054fb";
+char *B_Q_hex = "f95b9d7027be3950de9a050eba7301d5234ad89bf260d47e94a724b49759ab9a8fca22fe484e5e5ddf0845734cd3322d271e146e1e6eed6e16a2740c294097cd65deeacbfa563cce42065720836d421bcfd73c6dcab3aa0c4d480ac445e9ba11fb7825559b29ab4f9f6f079acbd0dc5c38702f386b3c95107540195a4508401b";
+
+int generate_key_material(csprng *RNG, key_material *km, octet *P, octet *Q)
+{
+    int rc;
+
+    char pk[2 * EFS_SECP256K1 + 1];
+    octet PK = {0, sizeof(pk), pk};
+
+    char out[2][FS_2048];
+    octet OUT1 = {0, sizeof(out[0]), out[0]};
+    octet OUT2 = {0, sizeof(out[1]), out[1]};
+
+    ECP_SECP256K1 ECP;
+
+    // ECDSA Key Pair
+    printf("\n\tGenerate ECDSA key pair\n");
+
+    rc = ECP_SECP256K1_KEY_PAIR_GENERATE(RNG, km->SK, &PK);
+    if (rc != 0)
+    {
+        return rc;
+    }
+
+    rc = ECP_SECP256K1_PUBLIC_KEY_VALIDATE(&PK);
+    if (rc != 0)
+    {
+        return rc;
+    }
+
+    ECP_SECP256K1_fromOctet(&ECP, &PK);
+    ECP_SECP256K1_toOctet(km->PK, &ECP, true);
+
+    printf("\t\tSK = ");
+    OCT_output(km->SK);
+    printf("\t\tPK = ");
+    OCT_output(km->PK);
+
+    // Paillier Key pair
+    printf("\n\tGenerate Paillier key pair. Associated primes\n");
+
+    PAILLIER_KEY_PAIR(RNG, NULL, NULL, &km->paillier_pk, &km->paillier_sk);
+
+    FF_2048_toOctet(&OUT1, km->paillier_sk.p, HFLEN_2048);
+    FF_2048_toOctet(&OUT2, km->paillier_sk.q, HFLEN_2048);
+
+    printf("\t\tP = ");
+    OCT_output(&OUT1);
+    printf("\t\tQ = ");
+    OCT_output(&OUT2);
+
+    // BC modulus
+    printf("\n\tGenerate BC modulus\n");
+
+    COMMITMENTS_BC_setup(RNG, &km->bc_sm, P, Q, NULL, NULL);
+    COMMITMENTS_BC_export_public_modulus(&km->bc_pm, &km->bc_sm);
+
+    FF_2048_toOctet(&OUT1, km->bc_sm.P, HFLEN_2048);
+    FF_2048_toOctet(&OUT2, km->bc_sm.Q, HFLEN_2048);
+
+    printf("\t\tP  = ");
+    OCT_output(&OUT1);
+    printf("\t\tQ  = ");
+    OCT_output(&OUT2);
+
+    FF_2048_toOctet(&OUT1, km->bc_sm.b0, FFLEN_2048);
+    FF_2048_toOctet(&OUT2, km->bc_sm.b1, FFLEN_2048);
+
+    printf("\t\tB0 = ");
+    OCT_output(&OUT1);
+    printf("\t\tB1 = ");
+    OCT_output(&OUT2);
+
+    return MPC_OK;
+}
+
+void key_material_zkp(csprng *RNG, key_material *km, octet *C, octet *P, octet *E, octet *Y)
+{
+    char r[EGS_SECP256K1];
+    octet R = {0, sizeof(r), r};
+
+    char s_e[EGS_SECP256K1];
+    octet S_E = {0, sizeof(s_e), s_e};
+
+    FACTORING_ZK_modulus m;
+
+    /* Prove knowledge of DLOG PK = s.G */
+
+    SCHNORR_commit(RNG, &R, C);
+    SCHNORR_challenge(km->PK, C, &S_E);
+    SCHNORR_prove(&R, &S_E, km->SK, P);
+
+    printf("\n\tProve knowledge of ECDSA sk\n");
+    printf("\t\tC = ");
+    OCT_output(C);
+    printf("\t\tE = ");
+    OCT_output(&S_E);
+    printf("\t\tP = ");
+    OCT_output(P);
+
+    OCT_clear(&R);
+
+    /* Prove knowledge of factorization of the Paillier modulus */
+
+    FF_2048_copy(m.p, km->paillier_sk.p, HFLEN_2048);
+    FF_2048_copy(m.q, km->paillier_sk.q, HFLEN_2048);
+    FF_2048_mul(m.n, m.p, m.q, HFLEN_2048);
+
+    FACTORING_ZK_prove(&m, RNG, NULL, E, Y);
+
+    printf("\n\tProve knowledge of the Paillier Secret Key\n");
+    printf("\t\tE = ");
+    OCT_output(E);
+    printf("\t\tY = ");
+    OCT_output(Y);
+
+    FACTORING_ZK_kill_modulus(&m);
+}
+
+int key_material_verify_zkp(key_material *km, octet *C, octet *P, octet *E, octet *Y)
+{
+    int rc;
+
+    char s_e[EGS_SECP256K1];
+    octet S_E = {0, sizeof(s_e), s_e};
+
+    char n[FS_2048];
+    octet N = {0, sizeof(n), n};
+
+    /* Verify Schnorr Proof for counterparty PK */
+    printf("\n\tVerify Proof of knowledge of ECDSA sk\n");
+
+    SCHNORR_challenge(km->CPK, C, &S_E);
+    rc = SCHNORR_verify(km->CPK, C, &S_E, P);
+    if (rc != SCHNORR_OK)
+    {
+        return rc;
+    }
+
+    printf("\t\tSuccess\n");
+
+    /* Verify Factoring Proof for Paillier PK */
+    printf("\n\tVerify Proof of knowledge of Paillier sk\n");
+
+    PAILLIER_PK_toOctet(&N, &km->paillier_cpk);
+
+    rc = FACTORING_ZK_verify(&N, E, Y);
+    if (rc != FACTORING_ZK_OK)
+    {
+        return rc;
+    }
+
+    printf("\t\tSuccess\n");
+
+    return MPC_OK;
+}
+
+/* Key Setup.
+ *
+ * Step 1.  Generate ECDSA key pair, Paillier key pair and Bit Commitment modulus
+ * Setp 1A. Commit to ECDSA public Key, generating commitment and decommitment values.
+ * Step 1B. Transmit Paillier public key, Bit Commitment public modulus and the commitment value
+ *
+ * Upon receipt of the commitment value from the other party:
+ *
+ * Step 2.  Decommit value for counterparty PK. Abort if it fails
+ * Step 2A. Combine public keys
+ *
+ * Step 3. Produce ZKP of correctness of key material and transmit
+ * Step 4. Verify counterparty proof of correctness of key material
+ */
+void key_setup(csprng *RNG, key_material *alice_km, key_material *bob_km)
+{
+    int rc;
+
+    char safe_p[HFS_2048];
+    octet SAFE_P = {0, sizeof(safe_p), safe_p};
+
+    char safe_q[HFS_2048];
+    octet SAFE_Q = {0, sizeof(safe_q), safe_q};
+
+    // Octets for Non Malleable Commitments
+    char commit_r[2][SHA256];
+    octet A_COMMIT_R = {0, sizeof(commit_r[0]), commit_r[0]};
+    octet B_COMMIT_R = {0, sizeof(commit_r[1]), commit_r[1]};
+
+    char commit_c[2][SHA256];
+    octet A_COMMIT_C = {0, sizeof(commit_c[0]), commit_c[0]};
+    octet B_COMMIT_C = {0, sizeof(commit_c[1]), commit_c[1]};
+
+    // Octets for Key Setup ZKPs
+    char kzkp_c[2][EFS_SECP256K1 + 1];
+    octet A_KZKP_C = {0, sizeof(kzkp_c[0]), kzkp_c[0]};
+    octet B_KZKP_C = {0, sizeof(kzkp_c[1]), kzkp_c[1]};
+
+    char kzkp_p[2][EGS_SECP256K1];
+    octet A_KZKP_P = {0, sizeof(kzkp_p[0]), kzkp_p[0]};
+    octet B_KZKP_P = {0, sizeof(kzkp_p[1]), kzkp_p[1]};
+
+    char kzkp_e[2][SHA256];
+    octet A_KZKP_E = {0, sizeof(kzkp_e[0]), kzkp_e[0]};
+    octet B_KZKP_E = {0, sizeof(kzkp_e[1]), kzkp_e[1]};
+
+    char kzkp_y[2][FS_2048];
+    octet A_KZKP_Y = {0, sizeof(kzkp_y[0]), kzkp_y[0]};
+    octet B_KZKP_Y = {0, sizeof(kzkp_y[1]), kzkp_y[1]};
+
+    // Octet for paillier PK transmission
+    char paillier_pk[FS_2048];
+    octet PAILLIER_PK = {0, sizeof(paillier_pk), paillier_pk};
+
+    /* Alice - generate key material and commitment */
+
+    printf("\n[Alice] Generate key material\n");
+
+    OCT_fromHex(&SAFE_P, A_P_hex);
+    OCT_fromHex(&SAFE_Q, A_Q_hex);
+
+    rc = generate_key_material(RNG, alice_km, &SAFE_P, &SAFE_Q);
+    if (rc != MPC_OK)
+    {
+        printf("\nFAILURE generating Alice key material. rc %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\n[Alice] Commit to generated PK\n");
+
+    COMMITMENTS_NM_commit(RNG, alice_km->PK, &A_COMMIT_R, &A_COMMIT_C);
+
+    printf("\tR = ");
+    OCT_output(&A_COMMIT_R);
+    printf("\tC = ");
+    OCT_output(&A_COMMIT_C);
+
+    printf("\n[Alice] Transmit commitment C\n");
+
+    // Transmit commitment C, Paillier PK and BC [u] modulus
+    PAILLIER_PK_toOctet(&PAILLIER_PK, &alice_km->paillier_pk);
+    PAILLIER_PK_fromOctet(&bob_km->paillier_cpk, &PAILLIER_PK);
+    COMMITMENTS_BC_export_public_modulus(&bob_km->bc_cpm, &alice_km->bc_sm);
+
+    /* Bob - generate key material and commitment */
+
+    printf("\n[Bob] Generate key material\n");
+
+    OCT_fromHex(&SAFE_P, B_P_hex);
+    OCT_fromHex(&SAFE_Q, B_Q_hex);
+
+    rc = generate_key_material(RNG, bob_km, &SAFE_P, &SAFE_Q);
+    if (rc != MPC_OK)
+    {
+        printf("\nFAILURE generating Bob key material. rc %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\n[Bob] Commit to generated PK\n");
+
+    COMMITMENTS_NM_commit(RNG, bob_km->PK, &B_COMMIT_R, &B_COMMIT_C);
+
+    printf("\tR = ");
+    OCT_output(&A_COMMIT_R);
+    printf("\tC = ");
+    OCT_output(&A_COMMIT_C);
+
+    printf("\n[Bob] Transmit commitment C\n");
+
+    // Transmit commitment C, Paillier PK and BC public modulus
+    PAILLIER_PK_toOctet(&PAILLIER_PK, &bob_km->paillier_pk);
+    PAILLIER_PK_fromOctet(&alice_km->paillier_cpk, &PAILLIER_PK);
+    COMMITMENTS_BC_export_public_modulus(&alice_km->bc_cpm, &bob_km->bc_sm);
+
+    /* Alice/Bob - transmit decommitment strings and message */
+
+    printf("\n[Alice] Transmit decommitment value R and PK\n");
+    printf("\n[Bob] Transmit decommitment value R and PK\n");
+
+    OCT_copy(alice_km->CPK, bob_km->PK);
+    OCT_copy(bob_km->CPK, alice_km->PK);
+
+    /* Alice - decommit Bob PK and combine */
+
+    printf("\n[Alice] Decommit Bob PK and combine full PK\n");
+    rc = COMMITMENTS_NM_decommit(alice_km->CPK, &B_COMMIT_R, &B_COMMIT_C);
+    if (rc != COMMITMENTS_OK)
+    {
+        printf("\n FAILURE decommitting Bob PK\n");
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tDecommitment successful\n");
+
+    rc = MPC_SUM_PK(alice_km->PK, alice_km->CPK, alice_km->FPK);
+    if (rc != MPC_OK)
+    {
+        printf("\nFAILURE combining ECDSA PK for Alice\n");
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tCombined PK = ");
+    OCT_output(alice_km->FPK);
+
+    /* Bob - decommit Alice PK */
+
+    printf("\n[Bob] Decommit Alice PK and combine full PK\n");
+    rc = COMMITMENTS_NM_decommit(bob_km->CPK, &A_COMMIT_R, &A_COMMIT_C);
+    if (rc != COMMITMENTS_OK)
+    {
+        printf("\n FAILURE decommitting Alice PK\n");
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tDecommitment successful\n");
+
+    rc = MPC_SUM_PK(bob_km->PK, bob_km->CPK, bob_km->FPK);
+    if (rc != MPC_OK)
+    {
+        printf("\nFAILURE combining ECDSA PK for Bob\n");
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tCombined PK = ");
+    OCT_output(bob_km->FPK);
+
+    /* Alice - generate key material ZKP */
+
+    printf("\n[Alice] Prove correctness of key material\n");
+    key_material_zkp(RNG, alice_km, &A_KZKP_C, &A_KZKP_P, &A_KZKP_E, &A_KZKP_Y);
+
+    printf("\n[Alice] Transmit C, P, E, Y\n");
+
+    /* Bob - generate key material ZKP */
+
+    printf("\n[Bob] Prove correctness of key material\n");
+    key_material_zkp(RNG, bob_km, &B_KZKP_C, &B_KZKP_P, &B_KZKP_E, &B_KZKP_Y);
+
+    printf("\n[Bob] Transmit C, P, E, Y\n");
+
+    /* Alice/Bob - verify key material ZKP */
+
+    printf("\n[Alice] Verify Key Material ZKP\n");
+    rc = key_material_verify_zkp(alice_km, &B_KZKP_C, &B_KZKP_P, &B_KZKP_E, &B_KZKP_Y);
+    if (rc != MPC_OK)
+    {
+        printf("\n FAILURE invalid ZKP for Bob key material. rc %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\n[Bob] Verify Key Material ZKP\n");
+    rc = key_material_verify_zkp(bob_km, &A_KZKP_C, &A_KZKP_P, &A_KZKP_E, &A_KZKP_Y);
+    if (rc != MPC_OK)
+    {
+        printf("\n FAILURE invalid ZKP for Alice key material. rc %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+}
+
+/* Perform MTA with Range Proof and Receiver ZK Proof
+ *
+ * Step 1.  Alice encrypts its share and proves it is in the appropriate range in ZK
+ * Step 2.  Bob verifies the ZK proof and aborts if the verification fails
+ * Step 2A. Bob homomorphically multiplies its share and adds an obfuscation value z
+ * Step 2B. Bob proves knowledge of z and range of its share
+ * Step 3.  Alice verifies the ZK proof and aborts if the verification fails
+ * Step 3A. Alice decrypts the obfuscated product.
+ */
+void mta(csprng *RNG, key_material *alice_km, key_material *bob_km, octet *K, octet *GAMMA, octet *ALPHA, octet *BETA, char *alice_name, char *bob_name)
+{
+    int rc;
+
+    char ca[FS_4096];
+    octet CA = {0, sizeof(ca), ca};
+
+    char cb[FS_4096];
+    octet CB = {0, sizeof(cb), cb};
+
+    char r[FS_4096];
+    octet R = {0, sizeof(r), r};
+
+    char z[EGS_SECP256K1];
+    octet Z = {0, sizeof(z), z};
+
+    char e[EGS_SECP256K1];
+    octet E = {0, sizeof(e), e};
+
+    MTA_RP_commitment    alice_rp_c;
+    MTA_RP_commitment_rv alice_rp_rv;
+    MTA_RP_proof         alice_rp_proof;
+
+    MTA_ZK_commitment    bob_zk_c;
+    MTA_ZK_commitment_rv bob_zk_rv;
+    MTA_ZK_proof         bob_zk_proof;
+
+    char out[5][FS_4096];
+    octet OUT1 = {0, sizeof(out[0]), out[0]};
+    octet OUT2 = {0, sizeof(out[1]), out[1]};
+    octet OUT3 = {0, sizeof(out[2]), out[2]};
+    octet OUT4 = {0, sizeof(out[3]), out[3]};
+    octet OUT5 = {0, sizeof(out[4]), out[4]};
+
+
+    /* Alice - Initiate MTA protocol and generate Range Proof */
+
+    printf("\n[%s] MTA Pass 1 with Range Proof\n", alice_name);
+
+    MPC_MTA_CLIENT1(RNG, &alice_km->paillier_pk, K, &CA, &R);
+
+    printf("\tCA = ");
+    OCT_output(&CA);
+
+    printf("\n\tRange Proof\n");
+
+    MTA_RP_commit(RNG, &alice_km->paillier_sk, &alice_km->bc_cpm, K, &alice_rp_c, &alice_rp_rv);
+    MTA_RP_challenge(&alice_km->paillier_pk, &alice_km->bc_cpm, &CA, &alice_rp_c, &E);
+    MTA_RP_prove(&alice_km->paillier_sk, &alice_rp_rv, K, &R, &E, &alice_rp_proof);
+
+    MTA_RP_commitment_toOctets(&OUT1, &OUT2, &OUT3, &alice_rp_c);
+    printf("\t\tZ  = ");
+    OCT_output(&OUT1);
+    printf("\t\tU  = ");
+    OCT_output(&OUT2);
+    printf("\t\tW  = ");
+    OCT_output(&OUT3);
+
+    printf("\t\tE  = ");
+    OCT_output(&E);
+
+    MTA_RP_proof_toOctets(&OUT1, &OUT2, &OUT3, &alice_rp_proof);
+    printf("\t\tS  = ");
+    OCT_output(&OUT1);
+    printf("\t\tS1 = ");
+    OCT_output(&OUT2);
+    printf("\t\tS2 = ");
+    OCT_output(&OUT3);
+
+    MTA_RP_commitment_rv_kill(&alice_rp_rv);
+    OCT_clear(&R);
+
+    // Transmit CA, Commitment and Proof
+    printf("\n[%s] Transmit CA and proof (Z, U, W, S, S1, S2)\n", alice_name);
+
+    /* Bob - Verify Range Proof and perform second step of MTA protocol */
+
+    OCT_clear(&E);
+    MTA_RP_challenge(&bob_km->paillier_cpk, &bob_km->bc_pm, &CA, &alice_rp_c, &E);
+
+    printf("\n[%s] Verify proof\n", bob_name);
+    printf("\tE = ");
+    OCT_output(&E);
+
+    rc = MTA_RP_verify(&bob_km->paillier_cpk, &bob_km->bc_sm, &CA, &E, &alice_rp_c, &alice_rp_proof);
+    if (rc != MTA_OK)
+    {
+        printf("FAILURE %s - MTA Invalid %s Range Proof. rc %d\n", bob_name, alice_name, rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tSuccess\n");
+
+    printf("\n[%s] MTA Pass 2 with ZK Proof\n", bob_name);
+
+    MPC_MTA_SERVER(RNG, &bob_km->paillier_cpk, GAMMA, &CA, &Z, &R, &CB, BETA);
+
+    printf("\tCB   = ");
+    OCT_output(&CB);
+    printf("\tBETA = ");
+    OCT_output(BETA);
+
+    printf("\n\tZK Proof\n");
+
+    MTA_ZK_commit(RNG, &bob_km->paillier_cpk, &bob_km->bc_cpm, GAMMA, &Z, &CA, &bob_zk_c, &bob_zk_rv);
+    MTA_ZK_challenge(&bob_km->paillier_cpk, &bob_km->bc_cpm, &CA, &CB, &bob_zk_c, &E);
+    MTA_ZK_prove(&bob_km->paillier_cpk, &bob_zk_rv, GAMMA, &Z, &R, &E, &bob_zk_proof);
+
+    MTA_ZK_commitment_toOctets(&OUT1, &OUT2, &OUT3, &OUT4, &OUT5, &bob_zk_c);
+    printf("\t\tZ  = ");
+    OCT_output(&OUT1);
+    printf("\t\tZ1 = ");
+    OCT_output(&OUT2);
+    printf("\t\tT  = ");
+    OCT_output(&OUT3);
+    printf("\t\tV  = ");
+    OCT_output(&OUT4);
+    printf("\t\tW  = ");
+    OCT_output(&OUT5);
+
+    printf("\t\tE  = ");
+    OCT_output(&E);
+
+    MTA_ZK_proof_toOctets(&OUT1, &OUT2, &OUT3, &OUT4, &OUT5, &bob_zk_proof);
+    printf("\t\tS  = ");
+    OCT_output(&OUT1);
+    printf("\t\tS1 = ");
+    OCT_output(&OUT2);
+    printf("\t\tS2 = ");
+    OCT_output(&OUT3);
+    printf("\t\tT1 = ");
+    OCT_output(&OUT4);
+    printf("\t\tT2 = ");
+    OCT_output(&OUT5);
+
+    MTA_ZK_commitment_rv_kill(&bob_zk_rv);
+    OCT_clear(&R);
+    OCT_clear(&Z);
+
+    // Transmit CB, Commitment and Proof
+    printf("\n[%s] Transmit CB and proof (Z, Z1, T, V, W, S, S1, S2, T1, T2)\n", bob_name);
+
+    /* Alice - Verify ZK proof and perform last step of MTA protocol */
+
+    OCT_clear(&E);
+    MTA_ZK_challenge(&alice_km->paillier_pk, &alice_km->bc_pm, &CA, &CB, &bob_zk_c, &E);
+
+    printf("\n[%s] Verify proof\n", alice_name);
+    printf("\tE = ");
+    OCT_output(&E);
+
+    rc = MTA_ZK_verify(&alice_km->paillier_sk, &alice_km->bc_sm, &CA, &CB, &E, &bob_zk_c, &bob_zk_proof);
+    if (rc != MTA_OK)
+    {
+        printf("FAILURE %s - MTA Invalid %s ZK Proof. rc %d\n", alice_name, bob_name, rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tSuccess\n");
+
+    printf("\n[%s] MTA Pass 3\n", alice_name);
+
+    MPC_MTA_CLIENT2(&alice_km->paillier_sk, &CB, ALPHA);
+
+    printf("ALPHA = ");
+    OCT_output(ALPHA);
+}
+
+/* Perform MTAWC with Range Proof and Receiver ZK Proof
+ *
+ * Step 1.  Alice encrypts its share and proves it is in the appropriate range in ZK
+ * Step 2.  Bob verifies the ZK proof and aborts if the verification fails
+ * Step 2A. Bob homomorphically multiplies its share and adds an obfuscation value z
+ * Step 2B. Bob proves knowledge of z and range of its share. It also proves that its
+ *          share is the exponent of a known DLOG.
+ * Step 3.  Alice verifies the ZK proof and aborts if the verification fails
+ * Step 3A. Alice decrypts the obfuscated product.
+ */
+void mtawc(csprng *RNG, key_material *alice_km, key_material *bob_km, octet *K, octet *ALPHA, octet *BETA, char *alice_name, char *bob_name)
+{
+    int rc;
+
+    char ca[FS_4096];
+    octet CA = {0, sizeof(ca), ca};
+
+    char cb[FS_4096];
+    octet CB = {0, sizeof(cb), cb};
+
+    char r[FS_4096];
+    octet R = {0, sizeof(r), r};
+
+    char z[EGS_SECP256K1];
+    octet Z = {0, sizeof(z), z};
+
+    char e[EGS_SECP256K1];
+    octet E = {0, sizeof(e), e};
+
+    MTA_RP_commitment    alice_rp_c;
+    MTA_RP_commitment_rv alice_rp_rv;
+    MTA_RP_proof         alice_rp_proof;
+
+    MTA_ZKWC_commitment    bob_zk_c;
+    MTA_ZKWC_commitment_rv bob_zk_rv;
+    MTA_ZKWC_proof         bob_zk_proof;
+
+    char out[6][FS_4096];
+    octet OUT1 = {0, sizeof(out[0]), out[0]};
+    octet OUT2 = {0, sizeof(out[1]), out[1]};
+    octet OUT3 = {0, sizeof(out[2]), out[2]};
+    octet OUT4 = {0, sizeof(out[3]), out[3]};
+    octet OUT5 = {0, sizeof(out[4]), out[4]};
+    octet OUT6 = {0, sizeof(out[5]), out[5]};
+
+    /* Alice - Initiate MTA protocol and generate Range Proof */
+
+    printf("\n[%s] MTAWC Pass 1 with Range Proof\n", alice_name);
+
+    MPC_MTA_CLIENT1(RNG, &alice_km->paillier_pk, K, &CA, &R);
+
+    printf("\tCA = ");
+    OCT_output(&CA);
+
+    printf("\n\tRange Proof\n");
+
+    MTA_RP_commit(RNG, &alice_km->paillier_sk, &alice_km->bc_cpm, K, &alice_rp_c, &alice_rp_rv);
+    MTA_RP_challenge(&alice_km->paillier_pk, &alice_km->bc_cpm, &CA, &alice_rp_c, &E);
+    MTA_RP_prove(&alice_km->paillier_sk, &alice_rp_rv, K, &R, &E, &alice_rp_proof);
+
+    MTA_RP_commitment_rv_kill(&alice_rp_rv);
+    MTA_RP_commitment_toOctets(&OUT1, &OUT2, &OUT3, &alice_rp_c);
+    printf("\t\tZ  = ");
+    OCT_output(&OUT1);
+    printf("\t\tU  = ");
+    OCT_output(&OUT2);
+    printf("\t\tW  = ");
+    OCT_output(&OUT3);
+
+    printf("\t\tE  = ");
+    OCT_output(&E);
+
+    MTA_RP_proof_toOctets(&OUT1, &OUT2, &OUT3, &alice_rp_proof);
+    printf("\t\tS  = ");
+    OCT_output(&OUT1);
+    printf("\t\tS1 = ");
+    OCT_output(&OUT2);
+    printf("\t\tS2 = ");
+    OCT_output(&OUT3);
+
+    MTA_RP_commitment_rv_kill(&alice_rp_rv);
+    OCT_clear(&R);
+
+    // Transmit CA, Commitment and Proof
+    printf("\n[%s] Transmit CA and proof (Z, U, W, S, S1, S2)\n", alice_name);
+
+    /* Bob - Verify Range Proof and perform second step of MTAWC protocol */
+
+    OCT_clear(&E);
+    MTA_RP_challenge(&bob_km->paillier_cpk, &bob_km->bc_pm, &CA, &alice_rp_c, &E);
+
+    printf("\n[%s] Verify proof\n", bob_name);
+    printf("\tE = ");
+    OCT_output(&E);
+
+    rc = MTA_RP_verify(&bob_km->paillier_cpk, &bob_km->bc_sm, &CA, &E, &alice_rp_c, &alice_rp_proof);
+    if (rc != MTA_OK)
+    {
+        printf("FAILURE %s - MTAWC Invalid %s Range Proof. rc %d\n", bob_name, alice_name, rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tSuccess\n");
+
+    printf("\n[%s] MTAWC Pass 2 with ZK Proof\n", bob_name);
+
+    MPC_MTA_SERVER(RNG, &bob_km->paillier_cpk, bob_km->SK, &CA, &Z, &R, &CB, BETA);
+
+    printf("\tCB   = ");
+    OCT_output(&CB);
+    printf("\tBETA = ");
+    OCT_output(BETA);
+
+    printf("\n\tZK Proof\n");
+
+    MTA_ZKWC_commit(RNG, &bob_km->paillier_cpk, &bob_km->bc_cpm, bob_km->SK, &Z, &CA, &bob_zk_c, &bob_zk_rv);
+    MTA_ZKWC_challenge(&bob_km->paillier_cpk, &bob_km->bc_cpm, &CA, &CB, bob_km->PK, &bob_zk_c, &E);
+    MTA_ZKWC_prove(&bob_km->paillier_cpk, &bob_zk_rv, bob_km->SK, &Z, &R, &E, &bob_zk_proof);
+
+    MTA_ZKWC_commitment_toOctets(&OUT1, &OUT2, &OUT3, &OUT4, &OUT5, &OUT6, &bob_zk_c);
+    printf("\t\tU  = ");
+    OCT_output(&OUT1);
+    printf("\t\tZ  = ");
+    OCT_output(&OUT2);
+    printf("\t\tZ1 = ");
+    OCT_output(&OUT3);
+    printf("\t\tT  = ");
+    OCT_output(&OUT4);
+    printf("\t\tV  = ");
+    OCT_output(&OUT5);
+    printf("\t\tW  = ");
+    OCT_output(&OUT6);
+
+    printf("\t\tE  = ");
+    OCT_output(&E);
+
+    MTA_ZKWC_proof_toOctets(&OUT1, &OUT2, &OUT3, &OUT4, &OUT5, &bob_zk_proof);
+    printf("\t\tS  = ");
+    OCT_output(&OUT1);
+    printf("\t\tS1 = ");
+    OCT_output(&OUT2);
+    printf("\t\tS2 = ");
+    OCT_output(&OUT3);
+    printf("\t\tT1 = ");
+    OCT_output(&OUT4);
+    printf("\t\tT2 = ");
+    OCT_output(&OUT5);
+
+    MTA_ZKWC_commitment_rv_kill(&bob_zk_rv);
+    OCT_clear(&R);
+    OCT_clear(&Z);
+
+    // Transmit CB, Commitment and Proof
+    printf("\n[%s] Transmit CB and proof (Z, Z1, T, V, W, S, S1, S2, T1, T2)\n", bob_name);
+
+    /* Alice - Verify ZK proof and perform last step of MTAWC protocol */
+
+    OCT_clear(&E);
+    MTA_ZKWC_challenge(&alice_km->paillier_pk, &alice_km->bc_pm, &CA, &CB, alice_km->CPK, &bob_zk_c, &E);
+
+    printf("\n[%s] Verify proof\n", alice_name);
+    printf("\tE = ");
+    OCT_output(&E);
+
+    rc = MTA_ZKWC_verify(&alice_km->paillier_sk, &alice_km->bc_sm, &CA, &CB, alice_km->CPK, &E, &bob_zk_c, &bob_zk_proof);
+    if (rc != MTA_OK)
+    {
+        printf("FAILURE %s - MTAWC Invalid %s ZK Proof. rc %d\n", alice_name, bob_name, rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tSuccess\n");
+
+    printf("\n[%s] MTAWC Pass 3\n", alice_name);
+
+    MPC_MTA_CLIENT2(&alice_km->paillier_sk, &CB, ALPHA);
+
+    printf("ALPHA = ");
+    OCT_output(ALPHA);
+}
+
+/* Phase 5 interactive proof of consistency of the signature shares
+ *
+ * Step 1.  Each player generates random values phi and rho and commitments
+ *          V, A and commits to the value (V, A)
+ * Step 2.  The values (V, A) are decommited and the players prove they are
+ *          well formed
+ * Step 2A. The well formedness proofs are transmitted and verified
+ * Step 3.  Each player generates a proof for the commitments (V, A) and transmits
+ *          a commitment to the proof
+ * Step 3A. The proofs are decommitted and verified
+ */
+void phase5(csprng *RNG, octet *RP1, octet *RP2, octet *R1, octet *R2, octet *HM, octet *S1, octet *S2, octet *PK)
+{
+    int rc;
+
+    char rho[2][EGS_SECP256K1];
+    octet RHO1 = {0, sizeof(rho[0]), rho[0]};
+    octet RHO2 = {0, sizeof(rho[1]), rho[1]};
+
+    char phi[2][EGS_SECP256K1];
+    octet PHI1 = {0, sizeof(phi[0]), phi[0]};
+    octet PHI2 = {0, sizeof(phi[1]), phi[1]};
+
+    char v[2][EFS_SECP256K1 + 1];
+    octet V1 = {0, sizeof(v[0]), v[0]};
+    octet V2 = {0, sizeof(v[1]), v[1]};
+    octet *V[2] = {&V1, &V2};
+
+    char a[2][EFS_SECP256K1 + 1];
+    octet A1 = {0, sizeof(a[0]), a[0]};
+    octet A2 = {0, sizeof(a[1]), a[1]};
+    octet *A[2] = {&A1, &A2};
+
+    char u[2][EFS_SECP256K1 + 1];
+    octet U1 = {0, sizeof(u[0]), u[0]};
+    octet U2 = {0, sizeof(u[1]), u[1]};
+    octet *U[2] = {&U1, &U2};
+
+    char t[2][EFS_SECP256K1 + 1];
+    octet T1 = {0, sizeof(t[0]), t[0]};
+    octet T2 = {0, sizeof(t[1]), t[1]};
+    octet *T[2] = {&T1, &T2};
+
+    // Octets for Non Malleable Commitments
+    char double_ecp[2 * EFS_SECP256K1 + 2];
+    octet DOUBLE_ECP = {0, sizeof(double_ecp), double_ecp};
+
+    char commit_r[2][SHA256];
+    octet A_COMMIT_R = {0, sizeof(commit_r[0]), commit_r[0]};
+    octet B_COMMIT_R = {0, sizeof(commit_r[1]), commit_r[1]};
+
+    char commit_c[2][SHA256];
+    octet A_COMMIT_C = {0, sizeof(commit_c[0]), commit_c[0]};
+    octet B_COMMIT_C = {0, sizeof(commit_c[1]), commit_c[1]};
+
+    // Octets for Schnorr Proofs
+    char schnorr_r[2][EGS_SECP256K1];
+    octet SCHNORR_R1 = {0, sizeof(schnorr_r[0]), schnorr_r[0]};
+    octet SCHNORR_R2 = {0, sizeof(schnorr_r[1]), schnorr_r[1]};
+
+    char schnorr_e[2][EGS_SECP256K1];
+    octet SCHNORR_E1 = {0, sizeof(schnorr_e[0]), schnorr_e[0]};
+    octet SCHNORR_E2 = {0, sizeof(schnorr_e[1]), schnorr_e[1]};
+
+    char schnorr_c[2][EFS_SECP256K1 + 1];
+    octet SCHNORR_C1 = {0, sizeof(schnorr_c[0]), schnorr_c[0]};
+    octet SCHNORR_C2 = {0, sizeof(schnorr_c[1]), schnorr_c[1]};
+
+    char schnorr_p[2][EGS_SECP256K1];
+    octet SCHNORR_P1 = {0, sizeof(schnorr_p[0]), schnorr_p[0]};
+    octet SCHNORR_P2 = {0, sizeof(schnorr_p[1]), schnorr_p[1]};
+
+    char schnorr_a[2][EGS_SECP256K1];
+    octet SCHNORR_A1 = {0, sizeof(schnorr_a[0]), schnorr_a[0]};
+    octet SCHNORR_A2 = {0, sizeof(schnorr_a[1]), schnorr_a[1]};
+
+    char schnorr_b[2][EGS_SECP256K1];
+    octet SCHNORR_B1 = {0, sizeof(schnorr_b[0]), schnorr_b[0]};
+    octet SCHNORR_B2 = {0, sizeof(schnorr_b[1]), schnorr_b[1]};
+
+    char schnorr_d[2][EFS_SECP256K1 + 1];
+    octet SCHNORR_D1 = {0, sizeof(schnorr_d[0]), schnorr_d[0]};
+    octet SCHNORR_D2 = {0, sizeof(schnorr_d[1]), schnorr_d[1]};
+
+
+    char schnorr_t[2][EGS_SECP256K1];
+    octet SCHNORR_T1 = {0, sizeof(schnorr_t[0]), schnorr_t[0]};
+    octet SCHNORR_T2 = {0, sizeof(schnorr_t[1]), schnorr_t[1]};
+
+    char schnorr_u[2][EGS_SECP256K1];
+    octet SCHNORR_U1 = {0, sizeof(schnorr_u[0]), schnorr_u[0]};
+    octet SCHNORR_U2 = {0, sizeof(schnorr_u[1]), schnorr_u[1]};
+
+    /* Alice - commitment */
+
+    printf("\n[Alice] Generate commitment (V, A) for Phase5 proof and nm commit to it\n");
+
+    rc = MPC_PHASE5_commit(RNG, RP1, S1, &PHI1, &RHO1, &V1, &A1);
+    if (rc != MPC_OK)
+    {
+        fprintf(stderr, "FAILURE Alice Phase5 commit rc: %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tPHI = ");
+    OCT_output(&PHI1);
+    printf("\tRHO = ");
+    OCT_output(&RHO1);
+    printf("\tV   = ");
+    OCT_output(&V1);
+    printf("\tA   = ");
+    OCT_output(&A1);
+
+    // Generate commitment to V, A
+    printf("\n\tCommitment\n");
+
+    OCT_copy(&DOUBLE_ECP, &V1);
+    OCT_joctet(&DOUBLE_ECP, &A1);
+    COMMITMENTS_NM_commit(RNG, &DOUBLE_ECP, &A_COMMIT_R, &A_COMMIT_C);
+
+    printf("\t\tR = ");
+    OCT_output(&A_COMMIT_R);
+    printf("\t\tC = ");
+    OCT_output(&A_COMMIT_C);
+
+    printf("\n[Alice] Transmit C value for the (V, A) NM commitment\n");
+
+    /* Bob - commitment */
+
+    printf("\n[Bob] Generate commitment (V, A) for Phase5 proof and nm commit to it\n");
+
+    rc = MPC_PHASE5_commit(RNG, RP2, S2, &PHI2, &RHO2, &V2, &A2);
+    if (rc != MPC_OK)
+    {
+        fprintf(stderr, "FAILURE Bob Phase5 commit rc: %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tPHI = ");
+    OCT_output(&PHI2);
+    printf("\tRHO = ");
+    OCT_output(&RHO2);
+    printf("\tV   = ");
+    OCT_output(&V2);
+    printf("\tA   = ");
+    OCT_output(&A2);
+
+    // Generate commitment to V, A
+    printf("\n\tCommitment\n");
+
+    OCT_copy(&DOUBLE_ECP, &V2);
+    OCT_joctet(&DOUBLE_ECP, &A2);
+    COMMITMENTS_NM_commit(RNG, &DOUBLE_ECP, &B_COMMIT_R, &B_COMMIT_C);
+
+    printf("\t\tR = ");
+    OCT_output(&B_COMMIT_R);
+    printf("\t\tC = ");
+    OCT_output(&B_COMMIT_C);
+
+    printf("\n[Bob] Transmit C value for the (V, A) NM commitment\n");
+
+    // Decommit commitments
+    printf("\n[Alice] Transmit decommitment value R and (V, A)\n");
+    printf("\n[Bob] Transmit decommitment value R and (V, A)\n");
+
+    printf("\n[Alice] Decommit Bob (V, A)\n");
+
+    OCT_copy(&DOUBLE_ECP, &V2);
+    OCT_joctet(&DOUBLE_ECP, &A2);
+    rc = COMMITMENTS_NM_decommit(&DOUBLE_ECP, &B_COMMIT_R, &B_COMMIT_C);
+    if (rc != COMMITMENTS_OK)
+    {
+        printf("\nFAILURE - Invalid Bob (V, A) commitment\n");
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tSuccess\n");
+
+    printf("\n[Bob] Decommit Alice (V, A)\n");
+
+    OCT_copy(&DOUBLE_ECP, &V1);
+    OCT_joctet(&DOUBLE_ECP, &A1);
+    rc = COMMITMENTS_NM_decommit(&DOUBLE_ECP, &A_COMMIT_R, &A_COMMIT_C);
+    if (rc != COMMITMENTS_OK)
+    {
+        printf("\nFAILURE - Invalid Alice (V, A) commitment\n");
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tSuccess\n");
+
+    /* Alice/Bob - Prove well formedness of commitments (V, A) */
+
+    printf("\n[Alice] Generate DSchnorr Proof for PHI, SK, V\n");
+
+    SCHNORR_D_commit(RNG, RP1, &SCHNORR_A1, &SCHNORR_B1, &SCHNORR_D1);
+    SCHNORR_D_challenge(RP1, &V1, &SCHNORR_D1, &SCHNORR_E1);
+    SCHNORR_D_prove(&SCHNORR_A1, &SCHNORR_B1, &SCHNORR_E1, S1, &PHI1, &SCHNORR_T1, &SCHNORR_U1);
+
+    printf("\tD = ");
+    OCT_output(&SCHNORR_D1);
+    printf("\tE = ");
+    OCT_output(&SCHNORR_E1);
+    printf("\tT = ");
+    OCT_output(&SCHNORR_T1);
+    printf("\tU = ");
+    OCT_output(&SCHNORR_U1);
+
+    printf("\n[Alice] Generate Schnorr Proof for A, RHO\n");
+
+    SCHNORR_commit(RNG, &SCHNORR_R1, &SCHNORR_C1);
+    SCHNORR_challenge(&A1, &SCHNORR_C1, &SCHNORR_E1);
+    SCHNORR_prove(&SCHNORR_R1, &SCHNORR_E1, &RHO1, &SCHNORR_P1);
+
+    printf("\tC = ");
+    OCT_output(&SCHNORR_C1);
+    printf("\tE = ");
+    OCT_output(&SCHNORR_E1);
+    printf("\tP = ");
+    OCT_output(&SCHNORR_P1);
+
+    printf("\n[Alice] Transmit proofs (D, T, U) and (C, P)\n");
+
+    printf("\n[Bob] Generate DSchnorr Proof for PHI, SK, V\n");
+
+    SCHNORR_D_commit(RNG, RP2, &SCHNORR_A2, &SCHNORR_B2, &SCHNORR_D2);
+    SCHNORR_D_challenge(RP2, &V2, &SCHNORR_D2, &SCHNORR_E2);
+    SCHNORR_D_prove(&SCHNORR_A2, &SCHNORR_B2, &SCHNORR_E2, S2, &PHI2, &SCHNORR_T2, &SCHNORR_U2);
+
+    printf("\tC = ");
+    OCT_output(&SCHNORR_D2);
+    printf("\tE = ");
+    OCT_output(&SCHNORR_E2);
+    printf("\tT = ");
+    OCT_output(&SCHNORR_T2);
+    printf("\tU = ");
+    OCT_output(&SCHNORR_U2);
+
+    printf("\n[Bob] Generate Schnorr Proof for A, RHO\n");
+
+    SCHNORR_commit(RNG, &SCHNORR_R2, &SCHNORR_C2);
+    SCHNORR_challenge(&A2, &SCHNORR_C2, &SCHNORR_E2);
+    SCHNORR_prove(&SCHNORR_R2, &SCHNORR_E2, &RHO2, &SCHNORR_P2);
+
+    printf("\tC = ");
+    OCT_output(&SCHNORR_C2);
+    printf("\tE = ");
+    OCT_output(&SCHNORR_E2);
+    printf("\tP = ");
+    OCT_output(&SCHNORR_P2);
+
+    printf("\n[Bob] Transmit proofs (D, T, U) and (C, P)\n");
+
+    printf("\n[Alice] Verify well formedness of Bob commitment (V, A)\n");
+
+    printf("\tVerify Proof for V\n");
+
+    SCHNORR_D_challenge(RP2, &V2, &SCHNORR_D2, &SCHNORR_E2);
+    printf("\t\tE = ");
+    OCT_output(&SCHNORR_E2);
+
+    rc = SCHNORR_D_verify(RP2, &V2, &SCHNORR_D2, &SCHNORR_E2, &SCHNORR_T2, &SCHNORR_U2);
+    if (rc != SCHNORR_OK)
+    {
+        printf("\nFAILURE - Invalid Bob V DSchnorr proof rc: %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\t\tSuccess\n");
+
+    printf("\n\tVerify Proof for A\n");
+
+    SCHNORR_challenge(&A2, &SCHNORR_C2, &SCHNORR_E2);
+    printf("\t\tE = ");
+    OCT_output(&SCHNORR_E2);
+
+    rc = SCHNORR_verify(&A2, &SCHNORR_C2, &SCHNORR_E2, &SCHNORR_P2);
+    if (rc != SCHNORR_OK)
+    {
+        printf("\nFAILURE - Invalid Bob A Schnorr proof rc: %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\t\tSuccess\n");
+
+    printf("\n[Bob] Verify well formedness of Alice commitment (V, A)\n");
+
+    printf("\tVerify Proof for V\n");
+
+    SCHNORR_D_challenge(RP1, &V1, &SCHNORR_D1, &SCHNORR_E1);
+    printf("\t\tE = ");
+    OCT_output(&SCHNORR_E1);
+
+    rc = SCHNORR_D_verify(RP1, &V1, &SCHNORR_D1, &SCHNORR_E1, &SCHNORR_T1, &SCHNORR_U1);
+    if (rc != SCHNORR_OK)
+    {
+        printf("\nFAILURE - Invalid Alice V DSchnorr proof rc: %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\t\tSuccess\n");
+
+    printf("\n\tVerify Proof for A\n");
+
+    SCHNORR_challenge(&A1, &SCHNORR_C1, &SCHNORR_E1);
+    printf("\t\tE = ");
+    OCT_output(&SCHNORR_E1);
+
+    rc = SCHNORR_verify(&A1, &SCHNORR_C1, &SCHNORR_E1, &SCHNORR_P1);
+    if (rc != SCHNORR_OK)
+    {
+        printf("\nFAILURE - Invalid Alice A Schnorr proof\n");
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\t\tSuccess\n");
+
+    /* Alice/Bob - proof */
+
+    printf("\n[Alice] Generate and commit to proof for commitments {V1, V2} and {A1, A2}\n");
+
+    rc = MPC_PHASE5_prove(&PHI1, &RHO1, V, A, PK, HM, R1, &U1, &T1);
+    if (rc != MPC_OK)
+    {
+        fprintf(stderr, "FAILURE Alice Phase5 prove rc: %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tU = ");
+    OCT_output(&U1);
+    printf("\tT = ");
+    OCT_output(&T1);
+
+    // Generate commitment to U, T
+    printf("\n\tCommitment\n");
+
+    OCT_copy(&DOUBLE_ECP, &U1);
+    OCT_joctet(&DOUBLE_ECP, &T1);
+    COMMITMENTS_NM_commit(RNG, &DOUBLE_ECP, &A_COMMIT_R, &A_COMMIT_C);
+
+    printf("\t\tR = ");
+    OCT_output(&A_COMMIT_R);
+    printf("\t\tC = ");
+    OCT_output(&A_COMMIT_C);
+
+    printf("\n[Alice] Transmit C value for the (U, T) NM commitment\n");
+
+    printf("\n[Bob] Generate and proof for commitments {V1, V2} and {A1, A2}\n");
+
+    rc = MPC_PHASE5_prove(&PHI2, &RHO2, V, A, PK, HM, R2, &U2, &T2);
+    if (rc != MPC_OK)
+    {
+        fprintf(stderr, "FAILURE Alice Phase5 prove rc: %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tU = ");
+    OCT_output(&U2);
+    printf("\tT = ");
+    OCT_output(&T2);
+
+    // Generate commitment to U, T
+    printf("\n\tCommitment\n");
+
+    OCT_copy(&DOUBLE_ECP, &U2);
+    OCT_joctet(&DOUBLE_ECP, &T2);
+    COMMITMENTS_NM_commit(RNG, &DOUBLE_ECP, &B_COMMIT_R, &B_COMMIT_C);
+
+    printf("\t\tR = ");
+    OCT_output(&B_COMMIT_R);
+    printf("\t\tC = ");
+    OCT_output(&B_COMMIT_C);
+
+    printf("\n[Bob] Transmit C value for the (U, T) NM commitment\n");
+
+    // Decommit proofs
+    printf("\n[Alice] Transmit decommitment value R and (U, T)\n");
+    printf("\n[Bob] Transmit decommitment value R and (U, T)\n");
+
+    printf("\n[Alice] Decommit Bob (U, T)\n");
+
+    OCT_copy(&DOUBLE_ECP, &U2);
+    OCT_joctet(&DOUBLE_ECP, &T2);
+    rc = COMMITMENTS_NM_decommit(&DOUBLE_ECP, &B_COMMIT_R, &B_COMMIT_C);
+    if (rc != COMMITMENTS_OK)
+    {
+        printf("\nFAILURE - Invalid Bob (U, T) commitment\n");
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tSuccess\n");
+
+    printf("\n[Bob] Decommit Alice (U, T)\n");
+
+    OCT_copy(&DOUBLE_ECP, &U1);
+    OCT_joctet(&DOUBLE_ECP, &T1);
+    rc = COMMITMENTS_NM_decommit(&DOUBLE_ECP, &A_COMMIT_R, &A_COMMIT_C);
+    if (rc != COMMITMENTS_OK)
+    {
+        printf("\nFAILURE - Invalid Alice (U, T) commitment\n");
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tSuccess\n");
+
+    /* Alice/Bob - verify, same for both */
+
+    printf("\n[Alice - Bob] Verify proof {T1, T2}, {U1, U2}\n");
+
+    rc = MPC_PHASE5_verify(U, T);
+    if (rc != MPC_OK)
+    {
+        fprintf(stderr, "FAILURE Alice/Bob Phase5 verify rc: %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+}
+
+/* Signature.
+ *
+ * Step 1.  Each player generates random k, gamma and commits to gamma.G
+ * Step 2.  Each player performs a MTA with shares k_i, gamma_j
+ * Step 2A. Each player performs a MTAWC with shares k_i, sk_j
+ * Step 3.  Each player sums the output of the MTA runs with the product k_i * gamma_i
+ * Step 3A. The values from Step 3 are shared and combined by each player
+ * Step 4.  The values gamma_i.G are revealed and combined with the value from Step 3A
+ *          to compute the R component of the signature
+ * Step 5.  Each player sums the outputs of the MTAWC runs with the product k_i * sk_i
+ * Step 5A. Each player uses the value from Step 5 to compute its share of the s component of the signature
+ * Step 5B. The players engage in the Phase5 ZKP to check the consistency of the signature shares
+ * Step 5C. The players broadcast their signature shares and verify that the reconciled signature is valid
+ */
+void signature(csprng *RNG, octet *M, key_material *alice_km, key_material *bob_km)
+{
+    int rc;
+
+    // Random generation
+    BIG_256_56 q;
+    BIG_256_56 k1;
+    BIG_256_56 k2;
+
+    char oct_k[2][EGS_SECP256K1];
+    octet K1 = {0, sizeof(oct_k[0]), oct_k[0]};
+    octet K2 = {0, sizeof(oct_k[1]), oct_k[1]};
+
+    char gamma[2][EGS_SECP256K1];
+    octet GAMMA1 = {0, sizeof(gamma[0]), gamma[0]};
+    octet GAMMA2 = {0, sizeof(gamma[1]), gamma[1]};
+
+    char gammapt[2][EFS_SECP256K1 + 1];
+    octet GAMMAPT1 = {0, sizeof(gammapt[0]), gammapt[0]};
+    octet GAMMAPT2 = {0, sizeof(gammapt[1]), gammapt[1]};
+
+    // Octets for Non Malleable Commitments
+    char commit_r[2][SHA256];
+    octet A_COMMIT_R = {0, sizeof(commit_r[0]), commit_r[0]};
+    octet B_COMMIT_R = {0, sizeof(commit_r[1]), commit_r[1]};
+
+    char commit_c[2][SHA256];
+    octet A_COMMIT_C = {0, sizeof(commit_c[0]), commit_c[0]};
+    octet B_COMMIT_C = {0, sizeof(commit_c[1]), commit_c[1]};
+
+    // Octets for Schnorr Proofs
+    char schnorr_r[2][EGS_SECP256K1];
+    octet SCHNORR_R1 = {0, sizeof(schnorr_r[0]), schnorr_r[0]};
+    octet SCHNORR_R2 = {0, sizeof(schnorr_r[1]), schnorr_r[1]};
+
+    char schnorr_e[2][EGS_SECP256K1];
+    octet SCHNORR_E1 = {0, sizeof(schnorr_e[0]), schnorr_e[0]};
+    octet SCHNORR_E2 = {0, sizeof(schnorr_e[1]), schnorr_e[1]};
+
+    char schnorr_c[2][EFS_SECP256K1 + 1];
+    octet SCHNORR_C1 = {0, sizeof(schnorr_c[0]), schnorr_c[0]};
+    octet SCHNORR_C2 = {0, sizeof(schnorr_c[1]), schnorr_c[1]};
+
+    char schnorr_p[2][EGS_SECP256K1];
+    octet SCHNORR_P1 = {0, sizeof(schnorr_p[0]), schnorr_p[0]};
+    octet SCHNORR_P2 = {0, sizeof(schnorr_p[1]), schnorr_p[1]};
+
+    // Octets for MTA/WC
+    char alpha[2][EGS_SECP256K1];
+    octet ALPHA1 = {0, sizeof(alpha[0]), alpha[0]};
+    octet ALPHA2 = {0, sizeof(alpha[1]), alpha[1]};
+
+    char beta[2][EGS_SECP256K1];
+    octet BETA1 = {0, sizeof(beta[0]), beta[0]};
+    octet BETA2 = {0, sizeof(beta[1]), beta[1]};
+
+    // Octets for reconciliation
+    char delta[2][EGS_SECP256K1];
+    octet DELTA1 = {0, sizeof(delta[0]), delta[0]};
+    octet DELTA2 = {0, sizeof(delta[1]), delta[1]};
+
+    char ikgamma[2][EGS_SECP256K1];
+    octet IKGAMMA1 = {0, sizeof(ikgamma[0]), ikgamma[0]};
+    octet IKGAMMA2 = {0, sizeof(ikgamma[1]), ikgamma[1]};
+
+    char sigr[2][EGS_SECP256K1];
+    octet SIGR1 = {0, sizeof(sigr[0]), sigr[0]};
+    octet SIGR2 = {0, sizeof(sigr[1]), sigr[1]};
+
+    char sigrp[2][EFS_SECP256K1 + 1];
+    octet SIGRP1 = {0, sizeof(sigrp[0]), sigrp[0]};
+    octet SIGRP2 = {0, sizeof(sigrp[1]), sigrp[1]};
+
+    char sigma[2][EGS_SECP256K1];
+    octet SIGMA1 = {0, sizeof(sigma[0]), sigma[0]};
+    octet SIGMA2 = {0, sizeof(sigma[1]), sigma[1]};
+
+    char hm[SHA256];
+    octet HM = {0, sizeof(hm), hm};
+
+    char sigs[3][EGS_SECP256K1];
+    octet SIGS1 = {0, sizeof(sigs[0]), sigs[0]};
+    octet SIGS2 = {0, sizeof(sigs[1]), sigs[1]};
+    octet SIGS = {0, sizeof(sigs[2]),  sigs[2]};
+
+    // ECP conversion workspace
+    char ncp[2 * EFS_SECP256K1 + 1];
+    octet NCP = {0, sizeof(ncp), ncp};
+
+    ECP_SECP256K1 P;
+
+    BIG_256_56_rcopy(q, CURVE_Order_SECP256K1);
+
+    /* Alice - Generate k and gamma and commit to gamma.G */
+
+    printf("\n[Alice] Generate random K and GAMMA and commit to GAMMA.G\n");
+
+    BIG_256_56_randomnum(k1, q, RNG);
+    BIG_256_56_toBytes(K1.val, k1);
+    K1.len = EGS_SECP256K1;
+
+    ECP_SECP256K1_KEY_PAIR_GENERATE(RNG, &GAMMA1, &NCP);
+    ECP_SECP256K1_fromOctet(&P, &NCP);
+    ECP_SECP256K1_toOctet(&GAMMAPT1, &P, true);
+
+    printf("\tK       = ");
+    OCT_output(&K1);
+    printf("\tGAMMA   = ");
+    OCT_output(&GAMMA1);
+    printf("\tGAMMA.G = ");
+    OCT_output(&GAMMAPT1);
+
+    COMMITMENTS_NM_commit(RNG, &GAMMAPT1, &A_COMMIT_R, &A_COMMIT_C);
+
+    printf("\n\tCommitment\n");
+    printf("\t\tR = ");
+    OCT_output(&A_COMMIT_R);
+    printf("\t\tC = ");
+    OCT_output(&A_COMMIT_C);
+
+    // Transmit commitment C
+    printf("\n[Alice] Transmit commitment value C\n");
+
+    /* Bob - Generate k and gamma and commit to gamma.G */
+
+    printf("\n[Bob] Generate random K and GAMMA and commit to GAMMA.G\n");
+
+    BIG_256_56_randomnum(k2, q, RNG);
+    BIG_256_56_toBytes(K2.val, k2);
+    K2.len = EGS_SECP256K1;
+
+    ECP_SECP256K1_KEY_PAIR_GENERATE(RNG, &GAMMA2, &NCP);
+    ECP_SECP256K1_fromOctet(&P, &NCP);
+    ECP_SECP256K1_toOctet(&GAMMAPT2, &P, true);
+
+    printf("\tK       = ");
+    OCT_output(&K2);
+    printf("\tGAMMA   = ");
+    OCT_output(&GAMMA2);
+    printf("\tGAMMA.G = ");
+    OCT_output(&GAMMAPT2);
+
+    COMMITMENTS_NM_commit(RNG, &GAMMAPT2, &B_COMMIT_R, &B_COMMIT_C);
+
+    printf("\n\tCommitment\n");
+    printf("\t\tR = ");
+    OCT_output(&B_COMMIT_R);
+    printf("\t\tC = ");
+    OCT_output(&B_COMMIT_C);
+
+
+    // Transmit commitment C
+    printf("\n[Bob] Transmit commitment value C\n");
+
+    /* Alice/Bob - Initiate MTA with shares k1, gamma2 and k2, gamma1 */
+
+    printf("\n[Alice-Bob] Run MTA with K1, GAMMA2\n");
+    mta(RNG, alice_km, bob_km, &K1, &GAMMA2, &ALPHA1, &BETA2, "Alice", "Bob");
+    printf("\n[Bob-Alice] Run MTA with K2, GAMMA1\n");
+    mta(RNG, bob_km, alice_km, &K2, &GAMMA1, &ALPHA2, &BETA1, "Bob", "Alice");
+
+    /* Alice/Bob - combine received shares to compute an additive share of kgamma */
+
+    printf("\n[Alice] Recombine additive shares to compute K1*GAMMA1 + ALPHA1 + BETA1\n");
+    MPC_SUM_MTA(&K1, &GAMMA1, &ALPHA1, &BETA1, &DELTA1);
+    printf("\tDELTA = ");
+    OCT_output(&DELTA1);
+
+    printf("\n[Bob] Recombine additive shares to compute K2*GAMMA2 + ALPHA2 + BETA2\n");
+    MPC_SUM_MTA(&K2, &GAMMA2, &ALPHA2, &BETA2, &DELTA2);
+    printf("\tDELTA = ");
+    OCT_output(&DELTA2);
+
+    /* Alice/Bob - Initiate MTAWC with shares k1, sk1 and k2, sk2 */
+
+    printf("\n[Alice-Bob] Run MTAWC with K1, SK2\n");
+    mtawc(RNG, alice_km, bob_km, &K1, &ALPHA1, &BETA2, "Alice", "Bob");
+    printf("\n[Bob-Alice] Run MTAWC with K2, SK1\n");
+    mtawc(RNG, bob_km, alice_km, &K2, &ALPHA2, &BETA1, "Bob", "Alice");
+
+    /* Alice/Bob - combine received shares to compute an additive share of kw */
+
+    printf("\n[Alice] Recombine additive shares to compute K1*SK1 + ALPHA1 + BETA1\n");
+    MPC_SUM_MTA(&K1, alice_km->SK, &ALPHA1, &BETA1, &SIGMA1);
+    printf("\tSIGMA = ");
+    OCT_output(&SIGMA1);
+
+    printf("\n[Bob] Recombine additive shares to compute K2*SK2 + ALPHA2 + BETA2\n");
+    MPC_SUM_MTA(&K2, bob_km->SK,   &ALPHA2, &BETA2, &SIGMA2);
+    printf("\tSIGMA = ");
+    OCT_output(&SIGMA2);
+
+    /* Alice/Bob - broadcast DELTA1, DELTA2 and compute (kgamma)^(-1) */
+
+    printf("\n[Alice] Transmit share of DELTA\n");
+    printf("\n[Bob] Transmit share of DELTA\n");
+
+    printf("\n[Alice] Combine DELTA shares and invert modulo curve order\n");
+    MPC_INVKGAMMA(&DELTA1, &DELTA2, &IKGAMMA1);
+    printf("\tIKGAMMA = ");
+    OCT_output(&IKGAMMA1);
+
+    printf("\n[Bob] Combine DELTA shares and invert modulo curve order\n");
+    MPC_INVKGAMMA(&DELTA1, &DELTA2, &IKGAMMA2);
+    printf("\tIKGAMMA = ");
+    OCT_output(&IKGAMMA2);
+
+    /* Alice - transmit decommitment and message for gamma.G and prove knowldege of gamma */
+
+    printf("\n[Alice] Generate Schnorr Proof for DLOG GAMMA, GAMMA.G\n");
+
+    SCHNORR_commit(RNG, &SCHNORR_R1, &SCHNORR_C1);
+    SCHNORR_challenge(&GAMMAPT1, &SCHNORR_C1, &SCHNORR_E1);
+    SCHNORR_prove(&SCHNORR_R1, &SCHNORR_E1, &GAMMA1, &SCHNORR_P1);
+
+    printf("\tC = ");
+    OCT_output(&SCHNORR_C1);
+    printf("\tE = ");
+    OCT_output(&SCHNORR_E1);
+    printf("\tP = ");
+    OCT_output(&SCHNORR_P1);
+
+    printf("\n[Alice] Transmit GAMMA.G with decommitment R and proof (C, P)\n");
+
+    /* Bob - transmit decommitment and message for gamma.G and prove knowldege of gamma */
+
+    printf("\n[Bob] Generate Schnorr Proof for DLOG GAMMA, GAMMA.G\n");
+
+    SCHNORR_commit(RNG, &SCHNORR_R2, &SCHNORR_C2);
+    SCHNORR_challenge(&GAMMAPT2, &SCHNORR_C2, &SCHNORR_E2);
+    SCHNORR_prove(&SCHNORR_R2, &SCHNORR_E2, &GAMMA2, &SCHNORR_P2);
+
+    printf("\tC = ");
+    OCT_output(&SCHNORR_C2);
+    printf("\tE = ");
+    OCT_output(&SCHNORR_E2);
+    printf("\tP = ");
+    OCT_output(&SCHNORR_P2);
+
+    printf("\n[Bob] Transmit GAMMA.G with decommitment R and proof (C, P)\n");
+
+    /* Alice - verify decommitment of gamma.G and Schnorr Proof */
+
+    printf("\n[Alice] Decommit GAMMA.G received from Bob\n");
+
+    rc = COMMITMENTS_NM_decommit(&GAMMAPT2, &B_COMMIT_R, &B_COMMIT_C);
+    if (rc != MPC_OK)
+    {
+        printf("\nFAILURE - Invalid Bob gamma.G commitment\n");
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tSuccess\n");
+
+    printf("\n[Alice] Verify Schnorr Proof for GAMMA.G\n");
+
+    OCT_clear(&SCHNORR_E2);
+    SCHNORR_challenge(&GAMMAPT2, &SCHNORR_C2, &SCHNORR_E2);
+
+    printf("\tE = ");
+    OCT_output(&SCHNORR_E2);
+
+    rc = SCHNORR_verify(&GAMMAPT2, &SCHNORR_C2, &SCHNORR_E2, &SCHNORR_P2);
+    if (rc != SCHNORR_OK)
+    {
+        printf("\nFAILURE - Invalid Bob gamma.G Schnorr proof\n");
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tSuccess\n");
+
+    /* Bob - verify decommitment of gamma.G and Schnorr Proof */
+
+    printf("\n[Bob] Decommit GAMMA.G received from Alice\n");
+
+    rc = COMMITMENTS_NM_decommit(&GAMMAPT1, &A_COMMIT_R, &A_COMMIT_C);
+    if (rc != COMMITMENTS_OK)
+    {
+        printf("\nFAILURE - Invalid Alice gamma.G commitment\n");
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tSuccess\n");
+
+    printf("\n[Bob] Verify Schnorr Proof for GAMMA.G\n");
+
+    OCT_clear(&SCHNORR_E1);
+    SCHNORR_challenge(&GAMMAPT1, &SCHNORR_C1, &SCHNORR_E1);
+
+    printf("\tE = ");
+    OCT_output(&SCHNORR_E1);
+
+    rc = SCHNORR_verify(&GAMMAPT1, &SCHNORR_C1, &SCHNORR_E1, &SCHNORR_P1);
+    if (rc != SCHNORR_OK)
+    {
+        printf("\nFAILURE - Invalid Alice gamma.G Schnorr proof\n");
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tSuccess\n");
+
+    /* Alice/Bob - reconcile R and get x component */
+
+    printf("\n[Alice] Reconcile R component of the signature\n");
+
+    rc = MPC_R(&IKGAMMA1, &GAMMAPT1, &GAMMAPT2, &SIGR1, &SIGRP1);
+    if (rc != MPC_OK)
+    {
+        fprintf(stderr, "FAILURE Alice recombining R rc: %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tR  = ");
+    OCT_output(&SIGR1);
+    printf("\tRP = ");
+    OCT_output(&SIGRP1);
+
+    printf("\n[Bob] Reconcile R component of the signature\n");
+
+    rc = MPC_R(&IKGAMMA1, &GAMMAPT1, &GAMMAPT2, &SIGR2, &SIGRP2);
+    if (rc != MPC_OK)
+    {
+        fprintf(stderr, "FAILURE Bob recombinig R rc: %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tR  = ");
+    OCT_output(&SIGR2);
+    printf("\tRP = ");
+    OCT_output(&SIGRP2);
+
+    /* Alice/Bob - compute shares for S */
+
+    MPC_HASH(HASH_TYPE_SECP256K1, M, &HM);
+
+    printf("\n[Alice] Generate share for signature S component\n");
+
+    rc = MPC_S(&HM, &SIGR1, &K1, &SIGMA1, &SIGS1);
+    if (rc != MPC_OK)
+    {
+        fprintf(stderr, "FAILURE computing Alice S share rc: %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tS = ");
+    OCT_output(&SIGS1);
+
+    printf("\n[Bob] Generate share for signature S component\n");
+
+    rc = MPC_S(&HM, &SIGR2, &K2, &SIGMA2, &SIGS2);
+    if (rc != MPC_OK)
+    {
+        fprintf(stderr, "FAILURE computing Bob S share rc: %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tS = ");
+    OCT_output(&SIGS2);
+
+    /* Phase 5 */
+
+    printf("\n[Alice-Bob] Interactively prove consistency of S shares\n");
+
+    phase5(RNG, &SIGRP1, &SIGRP2, &SIGR1, &SIGR2, &HM, &SIGS1, &SIGS2, alice_km->FPK);
+
+    /* Alice/Bob - broadcast shares and combine */
+
+    printf("\n[Alice-Bob] Reconcile S component of signature\n");
+
+    MPC_SUM_S(&SIGS1, &SIGS2, &SIGS);
+
+    printf("\tS = ");
+    OCT_output(&SIGS);
+
+    /* Check signature validity */
+
+    printf("\n[Alice-Bob] Verify reconciled signature\n");
+
+    rc =  MPC_ECDSA_VERIFY(&HM, alice_km->FPK, &SIGR1, &SIGS);
+    if (rc != MPC_OK)
+    {
+        fprintf(stderr, "FAILURE invalid Alice rc: %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    rc = MPC_ECDSA_VERIFY(&HM, bob_km->FPK, &SIGR2, &SIGS);
+    if (rc != MPC_OK)
+    {
+        fprintf(stderr, "FAILURE invalid Bob signature rc: %d\n", rc);
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\tSignature valid\n");
+    printf("\nSignature\n");
+    printf("\tR = ");
+    OCT_output(&SIGR1);
+    printf("\tS = ");
+    OCT_output(&SIGS);
+}
+
+int main()
+{
+    key_material alice_km;
+
+    char a_sk[EGS_SECP256K1];
+    char a_pk[EFS_SECP256K1 + 1];
+    char a_cpk[EFS_SECP256K1 + 1];
+    char a_fpk[EFS_SECP256K1 + 1];
+    octet A_SK  = {0, sizeof(a_sk),  a_sk};
+    octet A_PK  = {0, sizeof(a_pk),  a_pk};
+    octet A_CPK = {0, sizeof(a_cpk), a_cpk};
+    octet A_FPK = {0, sizeof(a_fpk), a_fpk};
+    alice_km.SK =  &A_SK;
+    alice_km.PK =  &A_PK;
+    alice_km.CPK = &A_CPK;
+    alice_km.FPK = &A_FPK;
+
+    key_material bob_km;
+
+    char b_sk[EGS_SECP256K1];
+    char b_pk[EFS_SECP256K1 + 1];
+    char b_cpk[EFS_SECP256K1 + 1];
+    char b_fpk[EFS_SECP256K1 + 1];
+    octet B_SK  = {0, sizeof(b_sk),  b_sk};
+    octet B_PK  = {0, sizeof(b_pk),  b_pk};
+    octet B_CPK = {0, sizeof(b_cpk), b_cpk};
+    octet B_FPK = {0, sizeof(b_fpk), b_fpk};
+    bob_km.SK =  &B_SK;
+    bob_km.PK =  &B_PK;
+    bob_km.CPK = &B_CPK;
+    bob_km.FPK = &B_FPK;
+
+    // Deterministic RNG for example
+    char seed[32] = {0};
+    csprng RNG;
+    RAND_seed(&RNG, 32, seed);
+
+    printf("MPC full flow example\n");
+
+    // Key setup phase
+    printf("\n ** Key Setup **\n");
+
+    key_setup(&RNG, &alice_km, &bob_km);
+
+    // Signature phase
+    printf("\n ** Signature **\n");
+
+    char* msg = "BANANA";
+    octet MSG = {0, sizeof(msg), msg};
+    printf("\nSign message '%s'\n", msg);
+
+    signature(&RNG, &MSG, &alice_km, &bob_km);
+
+    printf("\nDone\n");
+}
diff --git a/examples/example_r.c b/examples/example_r.c
index af1e2f1..43b78c5 100644
--- a/examples/example_r.c
+++ b/examples/example_r.c
@@ -479,7 +479,7 @@
     }
 
     // Calculate the R signature component
-    rc = MPC_R(&INVKGAMMA, &GAMMAPT1, &GAMMAPT2, &SIG_R);
+    rc = MPC_R(&INVKGAMMA, &GAMMAPT1, &GAMMAPT2, &SIG_R, NULL);
     if (rc)
     {
         fprintf(stderr, "FAILURE MPC_R rc: %d\n", rc);
diff --git a/include/amcl/factoring_zk.h b/include/amcl/factoring_zk.h
index bb853cd..b8738f3 100644
--- a/include/amcl/factoring_zk.h
+++ b/include/amcl/factoring_zk.h
@@ -35,8 +35,13 @@
 {
 #endif
 
+#ifndef FS_2048
 #define FS_2048 MODBYTES_1024_58 * FFLEN_2048  /**< 2048 field size in bytes */
+#endif
+
+#ifndef HFS_2048
 #define HFS_2048 MODBYTES_1024_58 * HFLEN_2048 /**< Half 2048 field size in bytes */
+#endif
 
 #define FACTORING_ZK_B 16 /**< Security parameter, length in bytes */
 
diff --git a/include/amcl/mpc.h b/include/amcl/mpc.h
index 1c3dd97..9e5045e 100644
--- a/include/amcl/mpc.h
+++ b/include/amcl/mpc.h
@@ -86,7 +86,8 @@
 
 /** \brief R component
  *
- *  Generate the ECDSA signature R component
+ *  Generate the ECDSA signature R component. It also outputs the ECP
+ *  associate to the R component if specified
  *
  *  <ol>
  *  <li> \f$ r_x, r_y = k^{-1}G \f$ where G is the group generator
@@ -97,9 +98,10 @@
  *  @param  GAMMAPT1          Actor 1 gamma point
  *  @param  GAMMAPT2          Actor 2 gamma point
  *  @param  R                 R component of the signature
+ *  @param  RP                ECP associated to the R component of the signature. Optional
  *  @return                   Returns 0 or else error code
  */
-int MPC_R(octet *INVKGAMMA, octet *GAMMAPT1, octet *GAMMAPT2, octet *R);
+int MPC_R(octet *INVKGAMMA, octet *GAMMAPT1, octet *GAMMAPT2, octet *R, octet *RP);
 
 /** \brief Hash the message value
  *
diff --git a/python/amcl_mpc.py b/python/amcl_mpc.py
index e138cbc..48ea776 100755
--- a/python/amcl_mpc.py
+++ b/python/amcl_mpc.py
@@ -100,7 +100,7 @@
 extern void MPC_MTA_SERVER(csprng *RNG, PAILLIER_public_key *PUB, octet *B, octet *CA, octet *Z, octet *R, octet *CB, octet *BETA);
 extern void MPC_SUM_MTA(octet *A, octet *B, octet *ALPHA, octet *BETA, octet *SUM);
 extern void MPC_INVKGAMMA(octet *KGAMMA1, octet *KGAMMA2, octet *INVKGAMMA);
-extern extern int MPC_R(octet *INVKGAMMA, octet *GAMMAPT1, octet *GAMMAPT2, octet *R);
+extern extern int MPC_R(octet *INVKGAMMA, octet *GAMMAPT1, octet *GAMMAPT2, octet *R, octet *RP);
 extern void MPC_HASH(int sha, octet *M, octet *HM);
 extern int MPC_S(octet *HM, octet *R, octet *K, octet *SIGMA, octet *S);
 extern void MPC_SUM_S(octet *S1, octet *S2, octet *S);
@@ -131,7 +131,7 @@
 HFS_2048 = 128
 FS_4096 = 512
 EGS_SECP256K1 = 32
-PTS_SECP256K1 = 2*EGS_SECP256K1 + 1
+EFS_SECP256K1 = 32
 SHA256 = 32
 curve_order = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
 
@@ -366,12 +366,12 @@
     else:
         ecdsa_sk1, ecdsa_sk1_val = make_octet(EGS_SECP256K1)        
 
-    ecdsa_pk1, ecdsa_pk1_val = make_octet(PTS_SECP256K1)                
+    ecdsa_pk1, ecdsa_pk1_val = make_octet(2 * EFS_SECP256K1)                
 
     rc = libamcl_curve_secp256k1.ECP_SECP256K1_KEY_PAIR_GENERATE(rng, ecdsa_sk1, ecdsa_pk1)
 
     ecdsa_sk2 = to_str(ecdsa_sk1)
-    ecdsa_pk2 = to_str(ecdsa_pk1)    
+    ecdsa_pk2 = to_str(ecdsa_pk1)
     
     return rc, ecdsa_pk2, ecdsa_sk2
 
@@ -573,9 +573,9 @@
 
     Returns::
 
-        r: R component of the signature
         rc: Zero for success or else an error code
-
+        r : R component of the signature
+        rp: ECP associated to R component of signature
     Raises:
 
     """
@@ -584,12 +584,14 @@
     gammapt21, gammapt21_val = make_octet(None, gammapt2)    
     
     r1, r1_val = make_octet(EGS_SECP256K1)
+    rp, rp_val = make_octet(EFS_SECP256K1 + 1)
     
-    rc = libamcl_mpc.MPC_R(invkgamma1, gammapt11, gammapt21, r1)
+    rc = libamcl_mpc.MPC_R(invkgamma1, gammapt11, gammapt21, r1, rp)
 
     r2 = to_str(r1)
+    rp_str = to_str(rp)
     
-    return rc, r2
+    return rc, r2, rp_str
 
 def mpc_hash(message):
     """Hash the message value
@@ -726,7 +728,7 @@
     pk11, pk11_val = make_octet(None, pk1)
     pk21, pk21_val = make_octet(None, pk2)    
     
-    pk1, pk1_val = make_octet(PTS_SECP256K1)
+    pk1, pk1_val = make_octet(EFS_SECP256K1 + 1)
 
     rc = libamcl_mpc.MPC_SUM_PK(pk11, pk21, pk1);
 
diff --git a/python/example_ecdsa.py b/python/example_ecdsa.py
index 8156f7d..6c99a72 100755
--- a/python/example_ecdsa.py
+++ b/python/example_ecdsa.py
@@ -84,7 +84,7 @@
 
     # Calculate the R signature component
 
-    rc, SIG_R = amcl_mpc.mpc_r(INVKGAMMA, GAMMAPT1, GAMMAPT2)
+    rc, SIG_R, _ = amcl_mpc.mpc_r(INVKGAMMA, GAMMAPT1, GAMMAPT2)
 
 
     # ALPHA1 + BETA2 = K1 * W2    
diff --git a/python/example_r.py b/python/example_r.py
index b438cf0..fbed92e 100755
--- a/python/example_r.py
+++ b/python/example_r.py
@@ -169,7 +169,7 @@
     assert INVKGAMMAGOLDEN_hex == INVKGAMMA_hex, f"expected {INVKGAMMAGOLDEN_hex} got {INVKGAMMA_hex}"      
     # Calculate the R signature component
 
-    rc, SIG_R = amcl_mpc.mpc_r(INVKGAMMA, GAMMAPT1, GAMMAPT2)
+    rc, SIG_R, _ = amcl_mpc.mpc_r(INVKGAMMA, GAMMAPT1, GAMMAPT2)
 
     SIG_R_hex = SIG_R.hex()        
     assert SIG_RGOLDEN_hex == SIG_R_hex, f"expected {SIG_RGOLDEN_hex} got {SIG_R_hex}"      
diff --git a/python/test_ecdsa.py b/python/test_ecdsa.py
index 56aea4a..98c85a0 100755
--- a/python/test_ecdsa.py
+++ b/python/test_ecdsa.py
@@ -92,7 +92,7 @@
 
             # Calculate the R signature component
 
-            rc, SIG_R = amcl_mpc.mpc_r(INVKGAMMA, GAMMAPT1, GAMMAPT2)
+            rc, SIG_R, _ = amcl_mpc.mpc_r(INVKGAMMA, GAMMAPT1, GAMMAPT2)
            
             # ALPHA1 + BETA2 = K1 * W2    
 
diff --git a/python/test_r.py b/python/test_r.py
index d479f8f..f3175ea 100755
--- a/python/test_r.py
+++ b/python/test_r.py
@@ -78,7 +78,7 @@
 
             # Calculate the R signature component
 
-            rc, sig_r = amcl_mpc.mpc_r(invkgamma, test['GAMMAPT1'], test['GAMMAPT2'])
+            rc, sig_r, _ = amcl_mpc.mpc_r(invkgamma, test['GAMMAPT1'], test['GAMMAPT2'])
             self.assertEqual(vector['SIG_R'], sig_r.hex())
             self.assertEqual(rc, 0)            
             
diff --git a/src/mpc.c b/src/mpc.c
index b41039d..80babcc 100644
--- a/src/mpc.c
+++ b/src/mpc.c
@@ -162,32 +162,30 @@
 {
     BIG_256_56 kgamma1;
     BIG_256_56 kgamma2;
-    BIG_256_56 kgamma;
-    BIG_256_56 invkgamma;
     BIG_256_56 q;
 
     // Curve order
     BIG_256_56_rcopy(q, CURVE_Order_SECP256K1);
 
     // Load values
-    BIG_256_56_fromBytes(kgamma1, KGAMMA1->val);
-    BIG_256_56_fromBytes(kgamma2, KGAMMA2->val);
+    BIG_256_56_fromBytesLen(kgamma1, KGAMMA1->val, KGAMMA1->len);
+    BIG_256_56_fromBytesLen(kgamma2, KGAMMA2->val, KGAMMA2->len);
 
     // kgamma = kgamma1 + kgamma2 mod q
-    BIG_256_56_add(kgamma, kgamma1, kgamma2);
-    BIG_256_56_mod(kgamma, q);
+    BIG_256_56_add(kgamma1, kgamma1, kgamma2);
+    BIG_256_56_mod(kgamma1, q);
 
     // invkgamma = kgamma^{-1}
-    BIG_256_56_invmodp(invkgamma, kgamma, q);
+    BIG_256_56_invmodp(kgamma1, kgamma1, q);
 
     // Output result
     INVKGAMMA->len = EGS_SECP256K1;
-    BIG_256_56_toBytes(INVKGAMMA->val, invkgamma);
+    BIG_256_56_toBytes(INVKGAMMA->val, kgamma1);
 }
 
 
 /* Calculate the r component of the signature */
-int MPC_R(octet *INVKGAMMA, octet *GAMMAPT1, octet *GAMMAPT2, octet *R)
+int MPC_R(octet *INVKGAMMA, octet *GAMMAPT1, octet *GAMMAPT2, octet *R, octet *RP)
 {
     BIG_256_56 invkgamma;
     BIG_256_56 q;
@@ -200,16 +198,16 @@
     BIG_256_56_rcopy(q, CURVE_Order_SECP256K1);
 
     // Load values
-    BIG_256_56_fromBytes(invkgamma, INVKGAMMA->val);
+    BIG_256_56_fromBytesLen(invkgamma, INVKGAMMA->val, INVKGAMMA->len);
 
     if (!ECP_SECP256K1_fromOctet(&gammapt1, GAMMAPT1))
     {
-        return 1;
+        return MPC_INVALID_ECP;
     }
 
     if (!ECP_SECP256K1_fromOctet(&gammapt2, GAMMAPT2))
     {
-        return 1;
+        return MPC_INVALID_ECP;
     }
 
     // gammapt1 + gammapt2
@@ -223,14 +221,19 @@
     BIG_256_56_mod(rx, q);
     if (BIG_256_56_iszilch(rx))
     {
-        return 1;
+        return MPC_FAIL;
     }
 
     // Output result
     R->len = EGS_SECP256K1;
     BIG_256_56_toBytes(R->val, rx);
 
-    return 0;
+    if (RP != NULL)
+    {
+        ECP_SECP256K1_toOctet(RP, &gammapt1, true);
+    }
+
+    return MPC_OK;
 }
 
 // Hash the message
@@ -271,14 +274,14 @@
     BIG_256_56_mod(s, q);
     if (BIG_256_56_iszilch(s))
     {
-        return 1;
+        return MPC_FAIL;
     }
 
     // Output result
     S->len = EGS_SECP256K1;
     BIG_256_56_toBytes(S->val, s);
 
-    return 0;
+    return MPC_OK;
 }
 
 /* Calculate sum of s components of signature  */
@@ -314,12 +317,12 @@
     // Load values
     if (!ECP_SECP256K1_fromOctet(&pk1, PK1))
     {
-        return 1;
+        return MPC_INVALID_ECP;
     }
 
     if (!ECP_SECP256K1_fromOctet(&pk2, PK2))
     {
-        return 1;
+        return MPC_INVALID_ECP;
     }
 
     // pk1 + pk2
@@ -328,7 +331,7 @@
     // Output result
     ECP_SECP256K1_toOctet(PK, &pk1, true);
 
-    return 0;
+    return MPC_OK;
 }
 
 int MPC_PHASE5_commit(csprng *RNG, octet *R, octet *S, octet *PHI, octet *RHO, octet *V, octet *A)
diff --git a/src/mta.c b/src/mta.c
index c8fba17..86f07fa 100644
--- a/src/mta.c
+++ b/src/mta.c
@@ -274,27 +274,27 @@
     BIG_256_56 q;
 
     // Curve order
-    BIG_256_56_rcopy(q,CURVE_Order_SECP256K1);
+    BIG_256_56_rcopy(q, CURVE_Order_SECP256K1);
 
     // Load values
-    BIG_256_56_fromBytes(a,A->val);
-    BIG_256_56_fromBytes(b,B->val);
-    BIG_256_56_fromBytes(alpha,ALPHA->val);
-    BIG_256_56_fromBytes(beta,BETA->val);
+    BIG_256_56_fromBytesLen(a, A->val, A->len);
+    BIG_256_56_fromBytesLen(b, B->val, B->len);
+    BIG_256_56_fromBytesLen(alpha, ALPHA->val, ALPHA->len);
+    BIG_256_56_fromBytesLen(beta, BETA->val, BETA->len);
 
     // sum = a.b mod q
-    BIG_256_56_modmul(sum,a,b,q);
+    BIG_256_56_modmul(sum, a, b, q);
 
     // sum = sum + alpha  + beta
-    BIG_256_56_add(sum,sum,alpha);
-    BIG_256_56_add(sum,sum,beta);
+    BIG_256_56_add(sum, sum, alpha);
+    BIG_256_56_add(sum, sum, beta);
 
     // sum = sum mod q
-    BIG_256_56_mod(sum,q);
+    BIG_256_56_mod(sum, q);
 
     // Output result
-    SUM->len=EGS_SECP256K1;
-    BIG_256_56_toBytes(SUM->val,sum);
+    SUM->len = EGS_SECP256K1;
+    BIG_256_56_toBytes(SUM->val, sum);
 
     // Clean memory
     BIG_256_56_zero(a);
@@ -866,7 +866,7 @@
 
     // Clean memory
     FF_2048_zero(hws, HFLEN_2048);
-    FF_2048_zero(ws , FFLEN_2048);
+    FF_2048_zero(ws, FFLEN_2048);
     FF_2048_zero(dws, 2 * FFLEN_2048);
 }
 
diff --git a/test/smoke/test_ecdsa_smoke.c b/test/smoke/test_ecdsa_smoke.c
index f1100a7..eb20072 100644
--- a/test/smoke/test_ecdsa_smoke.c
+++ b/test/smoke/test_ecdsa_smoke.c
@@ -169,7 +169,7 @@
 
     printf("Generating ECDSA key pair two\n");
     ECP_SECP256K1_KEY_PAIR_GENERATE(RNG,&W2,&NC_ECP);
-    
+
     // Convert to compressed form
     rc=ECP_SECP256K1_fromOctet(&P, &NC_ECP);
     if (!rc)
@@ -189,7 +189,7 @@
 
     printf("Generating GAMMA pair one\n");
     ECP_SECP256K1_KEY_PAIR_GENERATE(RNG,&GAMMA1,&NC_ECP);
-    
+
     // Convert to compressed form
     rc=ECP_SECP256K1_fromOctet(&P, &NC_ECP);
     if (!rc)
@@ -209,7 +209,7 @@
 
     printf("Generating GAMMA pair two\n");
     ECP_SECP256K1_KEY_PAIR_GENERATE(RNG,&GAMMA2,&NC_ECP);
-    
+
     // Convert to compressed form
     rc=ECP_SECP256K1_fromOctet(&P, &NC_ECP);
     if (!rc)
@@ -305,7 +305,7 @@
     printf("\n");
 
     // Calculate the R signature component
-    rc = MPC_R(&INVKGAMMA, &GAMMAPT1, &GAMMAPT2, &SIG_R);
+    rc = MPC_R(&INVKGAMMA, &GAMMAPT1, &GAMMAPT2, &SIG_R, NULL);
     if (rc)
     {
         fprintf(stderr, "FAILURE MPC_R rc: %d\n", rc);
diff --git a/test/smoke/test_r_smoke.c b/test/smoke/test_r_smoke.c
index 6103d41..44407ad 100644
--- a/test/smoke/test_r_smoke.c
+++ b/test/smoke/test_r_smoke.c
@@ -79,9 +79,13 @@
 
 char* INVKGAMMA_hex = "f9419b11580cdc098cbcbfd3ac06e70d2d4827447353f64bd97daa5667b34ef9";
 
-char* GAMMAPT1_hex = "03206b7c7ae7ecf8fe79ac581d8de90b9a12a27f79732268649fb0ae109faade73";
+char* GAMMAPT1_hex = "04206b7c7ae7ecf8fe79ac581d8de90b9a12a27f79732268649fb0ae109faade73385833cafe293b42e33097a79397c77c94a7d12a32304514a890a0c2d747a40d";
 
-char* GAMMAPT2_hex = "03fc86f69384e2b0cc3d563dc24ebb3a7ca0ac12dfa671e4cda4abdec35f33ed32";
+char* GAMMAPT2_hex = "04fc86f69384e2b0cc3d563dc24ebb3a7ca0ac12dfa671e4cda4abdec35f33ed326fdc2404c8e236d5bea82bcbe4aeeb7545c8b5d0a19a39e00bacf8a7143800a9";
+
+char* SUMGAMMAPT_hex = "04b46da316359aead5e06c983407b199465bad193dc661334aafb1d7d94bafe721e671defdf3eedef2b6f298f7cdc673a740e88dbb313f2afdb294ee6527e325c1";
+
+char* RPT_hex = "048adf50a4f51443cac2b4d488092ab49925da09e3feb57a1fc03b5b917ca6de9fdefc78277d8cb4865e3e4b17c2821017316d9b21e648e733a207aee22ec91b3c";
 
 char* SIG_R_hex = "8adf50a4f51443cac2b4d488092ab49925da09e3feb57a1fc03b5b917ca6de9f";
 
@@ -203,10 +207,10 @@
     char invkgammagolden[EGS_SECP256K1];
     octet INVKGAMMAGOLDEN = {0,sizeof(invkgammagolden),invkgammagolden};
 
-    char gammapt1[EFS_SECP256K1+1];
+    char gammapt1[2*EFS_SECP256K1+1];
     octet GAMMAPT1 = {0,sizeof(gammapt1),gammapt1};
 
-    char gammapt2[EFS_SECP256K1+1];
+    char gammapt2[2*EFS_SECP256K1+1];
     octet GAMMAPT2 = {0,sizeof(gammapt2),gammapt2};
 
     char sig_rgolden[EGS_SECP256K1];
@@ -477,15 +481,13 @@
     }
 
     // Calculate the R signature component
-    rc = MPC_R(&INVKGAMMA, &GAMMAPT1, &GAMMAPT2, &SIG_R);
+    rc = MPC_R(&INVKGAMMA, &GAMMAPT1, &GAMMAPT2, &SIG_R, NULL);
     if (rc)
     {
         fprintf(stderr, "FAILURE MPC_R rc: %d\n", rc);
         exit(EXIT_FAILURE);
     }
 
-    printf("%d", rc);
-
     printf("SIG_R: ");
     OCT_output(&SIG_R);
     printf("\n");
diff --git a/test/test.c b/test/test.c
index b739b04..7cd65fc 100644
--- a/test/test.c
+++ b/test/test.c
@@ -239,10 +239,10 @@
         fprintf(stderr, "FAILURE %s. Test %d\n", name, testNo);
 
 #ifdef DEBUG
-    printf("P = ");
-    ECP_SECP256K1_output(P);
-    printf("Q = ");
-    ECP_SECP256K1_output(Q);
+        printf("P = ");
+        ECP_SECP256K1_output(P);
+        printf("Q = ");
+        ECP_SECP256K1_output(Q);
 #endif
 
         exit(EXIT_FAILURE);
diff --git a/test/unit/test_r.c b/test/unit/test_r.c
index 03129bf..3a1b54f 100644
--- a/test/unit/test_r.c
+++ b/test/unit/test_r.c
@@ -229,7 +229,7 @@
             MPC_INVKGAMMA(&SUM1, &SUM2, &INVKGAMMA);
 
             // Calculate the R signature component
-            rc = MPC_R(&INVKGAMMA, &GAMMAPT1, &GAMMAPT2, &SIG_R);
+            rc = MPC_R(&INVKGAMMA, &GAMMAPT1, &GAMMAPT2, &SIG_R, NULL);
 
             sprintf(err_msg, "MPC_R rc: %d", rc);
             assert_tv(fp, testNo, err_msg, rc == 0);