You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@milagro.apache.org by sa...@apache.org on 2020/02/14 17:12:34 UTC

[incubator-milagro-MPC] 04/04: Add example for full flow

This is an automated email from the ASF dual-hosted git repository.

sandreoli pushed a commit to branch add-full-example
in repository https://gitbox.apache.org/repos/asf/incubator-milagro-MPC.git

commit 2ad9b1938aaf6ebf41ec98e69fe63d58455dbe73
Author: Samuele Andreoli <sa...@yahoo.it>
AuthorDate: Fri Feb 14 17:12:07 2020 +0000

    Add example for full flow
---
 examples/example_full.c | 1649 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 1649 insertions(+)

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");
+}